diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml new file mode 100644 index 0000000000..018dfae1ae --- /dev/null +++ b/.github/workflows/build_all.yml @@ -0,0 +1,56 @@ +name: Build all + +on: + push: + branches: + - main + paths: + - 'deps/**' + - 'src/**' + - '**/CMakeLists.txt' + - 'version.inc' + - 'localization/**' + - 'resources/**' + - ".github/workflows/build_*.yml" + + pull_request: + branches: + - main + paths: + - 'deps/**' + - 'src/**' + - '**/CMakeLists.txt' + - 'version.inc' + - ".github/workflows/build_*.yml" + + workflow_dispatch: # allows for manual dispatch + inputs: + build-deps-only: + description: 'Only build dependencies (bypasses caching)' + type: boolean + default: false + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + + +jobs: + build_all: + name: Build All + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-20.04 + - os: windows-latest + - os: macos-12 + arch: x86_64 + - os: macos-12 + arch: arm64 + uses: ./.github/workflows/build_check_cache.yml + with: + os: ${{ matrix.os }} + arch: ${{ matrix.arch }} + build-deps-only: ${{ inputs.build-deps-only || false }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/build_check_cache.yml b/.github/workflows/build_check_cache.yml new file mode 100644 index 0000000000..cccc084944 --- /dev/null +++ b/.github/workflows/build_check_cache.yml @@ -0,0 +1,58 @@ +name: Check Cache + +on: + workflow_call: + inputs: + os: + required: true + type: string + arch: + required: false + type: string + build-deps-only: + required: false + type: boolean + +jobs: + check_cache: # determines if there is a cache and outputs variables used in caching process + name: Check Cache + runs-on: ${{ inputs.os }} + outputs: + cache-key: ${{ steps.set_outputs.outputs.cache-key }} + cache-path: ${{ steps.set_outputs.outputs.cache-path }} + valid-cache: ${{ steps.cache_deps.outputs.cache-hit }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: set outputs + id: set_outputs + env: + underscore-arch: ${{ inputs.os == 'macos-12' && '_' || ''}}${{ inputs.os == 'macos-12' && inputs.arch || '' }} # if is macos, make a string that does "_{arch}", else output nothing + dash-arch: ${{ inputs.os == 'macos-12' && '-' || ''}}${{ inputs.os == 'macos-12' && inputs.arch || '' }} # if is macos, make a string that does "-{arch}", else output nothing + dep-folder-name: ${{ (inputs.os == 'windows-latest' || inputs.os == 'macos-12') && 'OrcaSlicer_dep' || 'destdir' }} + output-cmd: ${{ inputs.os == 'windows-latest' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}} + run: | + echo cache-key=${{ runner.os }}${{ env.dash-arch }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }} + echo cache-path=${{ github.workspace }}/deps/build${{ env.underscore-arch }}/${{ env.dep-folder-name }}${{ env.underscore-arch }} >> ${{ env.output-cmd }} + + - name: load cache + id: cache_deps + uses: actions/cache@v3 + with: + path: ${{ steps.set_outputs.outputs.cache-path }} + key: ${{ steps.set_outputs.outputs.cache-key }} + lookup-only: true + + build_deps: # call next step + name: Build Deps + needs: [check_cache] + uses: ./.github/workflows/build_deps.yml + with: + cache-key: ${{ needs.check_cache.outputs.cache-key }} + cache-path: ${{ needs.check_cache.outputs.cache-path }} + valid-cache: ${{ needs.check_cache.outputs.valid-cache == 'true' }} + os: ${{ inputs.os }} + arch: ${{ inputs.arch }} + build-deps-only: ${{ inputs.build-deps-only }} + secrets: inherit diff --git a/.github/workflows/build_deps.yml b/.github/workflows/build_deps.yml index 3bc9617973..e6a61029d5 100644 --- a/.github/workflows/build_deps.yml +++ b/.github/workflows/build_deps.yml @@ -1,60 +1,62 @@ -# name: Build Deps -name: Build deps - on: - pull_request: - branches: - - main - paths: - - 'deps/**' - - .github/workflows/build_deps.yml - push: - branches: - - main - paths: - - 'deps/**' - - .github/workflows/build_deps.yml - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + workflow_call: + inputs: + cache-key: + required: true + type: string + cache-path: + required: true + type: string + valid-cache: + required: true + type: boolean + os: + required: true + type: string + arch: + required: false + type: string + build-deps-only: + required: false + type: boolean jobs: build_deps: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-20.04 - - os: windows-latest - - os: macos-12 - arch: x86_64 - - os: macos-12 - arch: arm64 - runs-on: ${{ matrix.os }} - + name: Build Deps + if: inputs.build-deps-only || inputs.valid-cache != true + runs-on: ${{ inputs.os }} + env: + date: steps: + + # Setup the environment - name: Checkout uses: actions/checkout@v3 + + - name: load cached deps + uses: actions/cache@v3 + with: + path: ${{ inputs.cache-path }} + key: ${{ inputs.cache-key }} - name: setup dev on Windows - if: matrix.os == 'Windows' + if: inputs.os == 'windows-latest' uses: microsoft/setup-msbuild@v1.1 - name: Get the date on Ubuntu and macOS - if: matrix.os != 'windows-latest' - id: get-date-unix + if: inputs.os != 'windows-latest' run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV shell: bash - name: Get the date on Windows - if: matrix.os == 'windows-latest' - id: get-date-windows + if: inputs.os == 'windows-latest' run: echo "date=$(Get-Date -Format 'yyyyMMdd')" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 shell: pwsh + + # Build Dependencies - name: Build on Windows - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' working-directory: ${{ github.workspace }} run: | choco install strawberryperl @@ -64,26 +66,18 @@ jobs: .\build_release_vs2022.bat pack cd ${{ github.workspace }}/deps/build - - name: Build on Mac x86_64 - if: matrix.os == 'macos-12' && matrix.arch == 'x86_64' + - name: Build on Mac ${{ inputs.arch }} + if: inputs.os == 'macos-12' working-directory: ${{ github.workspace }} run: | brew install cmake git gettext automake - mkdir -p ${{ github.workspace }}/deps/build_x86_64 - mkdir -p ${{ github.workspace }}/deps/build_x86_64/OrcaSlicer_dep_x86_64 - ./build_release_macos.sh -dp -a x86_64 - - - name: Build on Mac arm64 - if: matrix.os == 'macos-12' && matrix.arch == 'arm64' - working-directory: ${{ github.workspace }} - run: | - brew install cmake git gettext automake - mkdir -p ${{ github.workspace }}/deps/build_arm64 - mkdir -p ${{ github.workspace }}/deps/build_arm64/OrcaSlicer_dep_arm64 - ./build_release_macos.sh -dp -a arm64 + brew list + mkdir -p ${{ github.workspace }}/deps/build_${{ inputs.arch }} + mkdir -p ${{ github.workspace }}/deps/build_${{ inputs.arch }}/OrcaSlicer_dep_${{ inputs.arch }} + ./build_release_macos.sh -dp -a ${{ inputs.arch }} - name: Build on Ubuntu - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' working-directory: ${{ github.workspace }} run: | sudo apt-get update @@ -99,32 +93,39 @@ jobs: ./BuildLinux.sh -dr cd deps/build tar -czvf OrcaSlicer_dep_ubuntu_$(date +"%Y%m%d").tar.gz destdir - - - name: Upload Mac arm64 artifacts - if: matrix.os == 'macos-12' && matrix.arch == 'arm64' - uses: actions/upload-artifact@v3 - with: - name: OrcaSlicer_dep_mac_arm64_${{ env.date }} - path: ${{ github.workspace }}/deps/build_arm64/OrcaSlicer_dep*.tar.gz - - name: Upload Mac x86_64 artifacts - if: matrix.os == 'macos-12' && matrix.arch == 'x86_64' + + # Upload Artifacts + - name: Upload Mac ${{ inputs.arch }} artifacts + if: inputs.os == 'macos-12' uses: actions/upload-artifact@v3 with: - name: OrcaSlicer_dep_mac_x86_64_${{ env.date }} - path: ${{ github.workspace }}/deps/build_x86_64/OrcaSlicer_dep*.tar.gz + name: OrcaSlicer_dep_mac_${{ inputs.arch }}_${{ env.date }} + path: ${{ github.workspace }}/deps/build_${{ inputs.arch }}/OrcaSlicer_dep*.tar.gz - name: Upload Windows artifacts - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' uses: actions/upload-artifact@v3 with: name: OrcaSlicer_dep_win64_${{ env.date }} path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep*.zip - name: Upload Ubuntu artifacts - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' uses: actions/upload-artifact@v3 with: name: OrcaSlicer_dep_ubuntu_${{ env.date }} path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep_ubuntu_*.tar.gz - \ No newline at end of file + + build_orca: + name: Build OrcaSlicer + needs: [build_deps] + if: ${{ !cancelled() && !inputs.build-deps-only && (inputs.valid-cache == true && needs.build_deps.result == 'skipped') || (inputs.valid-cache != true && success()) }} + uses: ./.github/workflows/build_orca.yml + with: + cache-key: ${{ inputs.cache-key }} + cache-path: ${{ inputs.cache-path }} + os: ${{ inputs.os }} + arch: ${{ inputs.arch }} + secrets: inherit + diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 0fd5724f9e..8084e61ee5 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -1,51 +1,33 @@ -name: Build OrcaSlicer - -on: - push: - branches: - - main - paths: - - 'src/**' - - '**/CMakeLists.txt' - - 'version.inc' - - 'localization/**' - - 'resources/**' - - ".github/workflows/build_orca.yml" - - pull_request: - branches: - - main - paths: - - 'src/**' - - '**/CMakeLists.txt' - - 'version.inc' - - ".github/workflows/build_orca.yml" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true +on: + workflow_call: + inputs: + cache-key: + required: true + type: string + cache-path: + required: true + type: string + os: + required: true + type: string + arch: + required: false + type: string jobs: build_orca: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-20.04 - - os: windows-latest - - os: macos-12 - arch: x86_64 - - os: macos-12 - arch: arm64 - runs-on: ${{ matrix.os }} + name: Build OrcaSlicer + runs-on: ${{ inputs.os }} + env: + date: + ver: steps: - name: Checkout uses: actions/checkout@v3 - name: Get the version and date on Ubuntu and macOS - if: matrix.os != 'windows-latest' - id: get-version-unix + if: inputs.os != 'windows-latest' run: | ver=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2) echo "ver=$ver" >> $GITHUB_ENV @@ -53,8 +35,7 @@ jobs: shell: bash - name: Get the version and date on Windows - if: matrix.os == 'windows-latest' - id: get-version-windows + if: inputs.os == 'windows-latest' run: | echo "date=$(Get-Date -Format 'yyyyMMdd')" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 # Extract the version from the file @@ -65,50 +46,30 @@ jobs: echo "ver=$ver" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 echo "date: ${{ env.date }} version: $ver" shell: pwsh - + + - name: load cached deps + uses: actions/cache@v3 + with: + path: ${{ inputs.cache-path }} + key: ${{ inputs.cache-key }} + # Mac - name: Install tools mac - if: matrix.os == 'macos-12' + if: inputs.os == 'macos-12' run: | brew install cmake git gettext zstd tree - mkdir -p ${{ github.workspace }}/deps/build_${{matrix.arch}} - mkdir -p ${{ github.workspace }}/deps/build_${{matrix.arch}}/OrcaSlicer_dep_${{matrix.arch}} - - # - name: build deps - # if: matrix.os == 'macos-12' - # id: cache_deps - # uses: actions/cache@v3 - # env: - # cache-name: ${{ runner.os }}-cache-orcaslicer_deps_${{matrix.arch}} - # with: - # path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep - # key: build-${{ env.cache-name }} - - # - if: ${{ steps.cache_deps.outputs.cache-hit != 'true' }} - # name: build deps - # working-directory: ${{ github.workspace }} - # continue-on-error: true - # run: ./build_release_macos.sh -d -a ${{matrix.arch}} - - name: Download and extract deps - if: matrix.os == 'macos-12' - working-directory: ${{ github.workspace }} - run: | - curl -LJO https://github.com/SoftFever/OrcaSlicer_deps/releases/download/OrcaSlicer_deps_Oct2023/OrcaSlicer_dep_mac_${{matrix.arch}}_20231008.tar.gz - tar -zvxf ./OrcaSlicer_dep_mac_${{matrix.arch}}_20231008.tar.gz -C ${{ github.workspace }}/deps/build_${{matrix.arch}} - chown -R $(id -u):$(id -g) ${{ github.workspace }}/deps/build_${{matrix.arch}} - tree ${{ github.workspace }}/deps/build_${{matrix.arch}} - rm ./OrcaSlicer_dep_mac_${{matrix.arch}}_20231008.tar.gz - + mkdir -p ${{ github.workspace }}/deps/build_${{inputs.arch}} + mkdir -p ${{ github.workspace }}/deps/build_${{inputs.arch}}/OrcaSlicer_dep_${{inputs.arch}} - name: Build slicer mac - if: matrix.os == 'macos-12' + if: inputs.os == 'macos-12' working-directory: ${{ github.workspace }} run: | - ./build_release_macos.sh -s -n -a ${{matrix.arch}} + ./build_release_macos.sh -s -n -a ${{inputs.arch}} # Thanks to RaySajuuk, it's working now - name: Sign app and notary - if: github.ref == 'refs/heads/main' && matrix.os == 'macos-12' + if: github.ref == 'refs/heads/main' && inputs.os == 'macos-12' working-directory: ${{ github.workspace }} env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} @@ -125,109 +86,86 @@ jobs: security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH - codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build_${{matrix.arch}}/OrcaSlicer/OrcaSlicer.app - ln -s /Applications ${{ github.workspace }}/build_${{matrix.arch}}/OrcaSlicer/Applications - hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build_${{matrix.arch}}/OrcaSlicer -ov -format UDZO OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg - codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg + codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build_${{inputs.arch}}/OrcaSlicer/OrcaSlicer.app + ln -s /Applications ${{ github.workspace }}/build_${{inputs.arch}}/OrcaSlicer/Applications + hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build_${{inputs.arch}}/OrcaSlicer -ov -format UDZO OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg + codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg xcrun notarytool store-credentials "notarytool-profile" --apple-id "${{ secrets.APPLE_DEV_ACCOUNT }}" --team-id "${{ secrets.TEAM_ID }}" --password "${{ secrets.APP_PWD }}" - xcrun notarytool submit "OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg" --keychain-profile "notarytool-profile" --wait - xcrun stapler staple OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg + xcrun notarytool submit "OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg" --keychain-profile "notarytool-profile" --wait + xcrun stapler staple OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg - name: Create DMG without notary - if: github.ref != 'refs/heads/main' && matrix.os == 'macos-12' + if: github.ref != 'refs/heads/main' && inputs.os == 'macos-12' working-directory: ${{ github.workspace }} run: | - ln -s /Applications ${{ github.workspace }}/build_${{matrix.arch}}/OrcaSlicer/Applications - hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build_${{matrix.arch}}/OrcaSlicer -ov -format UDZO OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg + ln -s /Applications ${{ github.workspace }}/build_${{inputs.arch}}/OrcaSlicer/Applications + hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build_${{inputs.arch}}/OrcaSlicer -ov -format UDZO OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg - name: Upload artifacts mac - if: matrix.os == 'macos-12' + if: inputs.os == 'macos-12' uses: actions/upload-artifact@v3 with: - name: OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }} - path: ${{ github.workspace }}/OrcaSlicer_Mac_${{matrix.arch}}_V${{ env.ver }}.dmg + name: OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }} + path: ${{ github.workspace }}/OrcaSlicer_Mac_${{inputs.arch}}_V${{ env.ver }}.dmg # Windows - name: setup MSVC - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' uses: microsoft/setup-msbuild@v1.1 - name: Install nsis - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' run: | dir "C:/Program Files (x86)/Windows Kits/10/Include" choco install nsis - - name: download deps - if: matrix.os == 'windows-latest' - shell: powershell - run: '(new-object System.Net.WebClient).DownloadFile("https://github.com/SoftFever/OrcaSlicer_deps/releases/download/OrcaSlicer_deps_Oct2023/OrcaSlicer_dep_win64_20230810_vs2022.zip", "$env:temp\OrcaSlicer_dep_win64_20230810_vs2022.zip")' - - - name: maker dir - if: matrix.os == 'windows-latest' - working-directory: ${{ github.workspace }} - run: | - mkdir ${{ github.workspace }}/deps/build - mkdir ${{ github.workspace }}/deps/build/OrcaSlicer_dep - - - name: extract deps - if: matrix.os == 'windows-latest' - working-directory: ${{ github.workspace }}/deps/build - shell: cmd - run: '"C:/Program Files/7-Zip/7z.exe" x %temp%\OrcaSlicer_dep_win64_20230810_vs2022.zip' - - # - name: build deps - # if: matrix.os == 'windows-latest' - # id: cache_deps - # uses: actions/cache@v3 - # env: - # cache-name: ${{ runner.os }}-cache-orcaslicer_deps - # with: - # path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep - # key: ${{ runner.os }}-build-${{ env.cache-name }} - - # - if: ${{ steps.cache_deps.outputs.cache-hit != 'true' }} - # name: build deps - # working-directory: ${{ github.workspace }} - # continue-on-error: true - # run: .\build_release_vs2022.bat deps - - # - run: Get-ChildItem ${{ github.workspace }}/deps/build/ -Exclude OrcaSlicer_dep | Remove-Item -Recurse -Force - - name: Build slicer Win - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' working-directory: ${{ github.workspace }} run: .\build_release_vs2022.bat slicer - name: Create installer Win - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' working-directory: ${{ github.workspace }}/build run: | cpack -G NSIS - # - name: pack app - # if: matrix.os == 'windows-latest' - # working-directory: ${{ github.workspace }}/build - # shell: cmd - # run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_dev_build.zip ${{ github.workspace }}/build/OrcaSlicer' + - name: Pack app + if: inputs.os == 'windows-latest' + working-directory: ${{ github.workspace }}/build + shell: cmd + run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_Windows_V${{ env.ver }}_portable.zip ${{ github.workspace }}/build/OrcaSlicer' + - name: Pack PDB + if: inputs.os == 'windows-latest' + working-directory: ${{ github.workspace }}/build/src/Release + shell: cmd + run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_V${{ env.ver }}_for_developers_only.7z *.pdb' + - name: Upload artifacts Win zip - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' uses: actions/upload-artifact@v3 with: name: OrcaSlicer_Windows_V${{ env.ver }}_portable - path: ${{ github.workspace }}/build/OrcaSlicer + path: ${{ github.workspace }}/build/OrcaSlicer_Windows_V${{ env.ver }}_portable.zip - name: Upload artifacts Win installer - if: matrix.os == 'windows-latest' + if: inputs.os == 'windows-latest' uses: actions/upload-artifact@v3 with: name: OrcaSlicer_Windows_V${{ env.ver }} path: ${{ github.workspace }}/build/OrcaSlicer*.exe -# Ubuntu + - name: Upload artifacts Win PDB + if: inputs.os == 'windows-latest' + uses: actions/upload-artifact@v3 + with: + name: PDB + path: ${{ github.workspace }}/build/src/Release/Debug_PDB_V${{ env.ver }}_for_developers_only.7z + +# Ubuntu - name: Install dependencies - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' run: | sudo apt-get update sudo apt-get install -y autoconf build-essential cmake curl eglexternalplatform-dev \ @@ -237,52 +175,25 @@ jobs: libwebkit2gtk-4.0-dev libxkbcommon-dev locales locales-all m4 pkgconf sudo wayland-protocols wget - name: Install dependencies from BuildLinux.sh - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' shell: bash run: sudo ./BuildLinux.sh -ur - name: Fix permissions - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' shell: bash run: sudo chown $USER -R ./ - # - name: Build deps - # if: matrix.os == 'ubuntu-20.04' - # id: cache_deps - # uses: actions/cache@v3 - # env: - # cache-name: ${{ runner.os }}-cache-orcaslicer_deps_x64 - # with: - # path: ${{ github.workspace }}/deps/build/destdir - # key: build-${{ env.cache-name }} - - # - if: ${{ steps.cache_deps.outputs.cache-hit != 'true' }} - # name: Build deps - # working-directory: ${{ github.workspace }} - # continue-on-error: true - # run: ./BuildLinux.sh -dr - - name: Download and extract deps - if: matrix.os == 'ubuntu-20.04' - working-directory: ${{ github.workspace }} - run: | - mkdir -p ${{ github.workspace }}/deps/build - mkdir -p ${{ github.workspace }}/deps/build/destdir - curl -LJO https://github.com/SoftFever/OrcaSlicer_deps/releases/download/OrcaSlicer_deps_Oct2023/OrcaSlicer_dep_ubuntu_20231008.zip - unzip ./OrcaSlicer_dep_ubuntu_20231008.zip -d ${{ github.workspace }}/deps/build/destdir - chown -R $(id -u):$(id -g) ${{ github.workspace }}/deps/build/destdir - ls -l ${{ github.workspace }}/deps/build/destdir - rm OrcaSlicer_dep_ubuntu_20231008.zip - - name: Build slicer - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' shell: bash run: | ./BuildLinux.sh -isr chmod +x ./build/OrcaSlicer_ubu64.AppImage - name: Upload artifacts Ubuntu - if: matrix.os == 'ubuntu-20.04' + if: inputs.os == 'ubuntu-20.04' uses: actions/upload-artifact@v3 with: name: OrcaSlicer_Linux_V${{ env.ver }} - path: './build/OrcaSlicer_ubu64.AppImage' + path: './build/OrcaSlicer_ubu64.AppImage' \ No newline at end of file diff --git a/.github/workflows/orca_bot.yml b/.github/workflows/orca_bot.yml index a60a9b6205..da92304040 100644 --- a/.github/workflows/orca_bot.yml +++ b/.github/workflows/orca_bot.yml @@ -13,11 +13,12 @@ jobs: - uses: actions/stale@v5 with: days-before-issue-stale: 90 - days-before-issue-close: 14 + days-before-issue-close: 7 operations-per-run: 1000 stale-issue-label: "stale" + ascending: true stale-issue-message: "GitHub bot: this issue is stale because it has been open for 90 days with no activity." - close-issue-message: "GitHub bot: This issue was closed because it has been inactive for 14 days since being marked as stale." + close-issue-message: "GitHub bot: This issue was closed because it has been inactive for 7 days since being marked as stale." days-before-pr-stale: -1 days-before-pr-close: -1 remove-issue-stale-when-updated: true diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 844466f3b5..f07cfacba9 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -133,6 +133,7 @@ else() ${_gen} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}/usr/local + -DCMAKE_PREFIX_PATH:STRING=${DESTDIR}/usr/local -DBUILD_SHARED_LIBS:BOOL=OFF ${_cmake_osx_arch} "${_configs_line}" diff --git a/deps/MPFR/MPFR.cmake b/deps/MPFR/MPFR.cmake index c29bb39ad0..f8e95cc808 100644 --- a/deps/MPFR/MPFR.cmake +++ b/deps/MPFR/MPFR.cmake @@ -30,7 +30,8 @@ else () URL_HASH SHA256=cf4f4b2d80abb79e820e78c8077b6725bbbb4e8f41896783c899087be0e94068 DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/MPFR BUILD_IN_SOURCE ON - CONFIGURE_COMMAND env "CFLAGS=${_gmp_ccflags}" "CXXFLAGS=${_gmp_ccflags}" ./configure ${_cross_compile_arg} --prefix=${DESTDIR}/usr/local --enable-shared=no --enable-static=yes --with-gmp=${DESTDIR}/usr/local ${_gmp_build_tgt} + CONFIGURE_COMMAND autoreconf -f -i && + env "CFLAGS=${_gmp_ccflags}" "CXXFLAGS=${_gmp_ccflags}" ./configure ${_cross_compile_arg} --prefix=${DESTDIR}/usr/local --enable-shared=no --enable-static=yes --with-gmp=${DESTDIR}/usr/local ${_gmp_build_tgt} BUILD_COMMAND make -j INSTALL_COMMAND make install DEPENDS dep_GMP diff --git a/deps/OpenSSL/OpenSSL.cmake b/deps/OpenSSL/OpenSSL.cmake index 7eb28af1a7..3dc103698e 100644 --- a/deps/OpenSSL/OpenSSL.cmake +++ b/deps/OpenSSL/OpenSSL.cmake @@ -19,7 +19,7 @@ if(WIN32) set(_install_cmd nmake install_sw ) else() if(APPLE) - set(_conf_cmd ./Configure ) + set(_conf_cmd export MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} && ./Configure ) else() set(_conf_cmd "./config") endif() diff --git a/doc/Chamber-temperature.md b/doc/Chamber-temperature.md index 2ab9c1b7cf..ea268532bf 100644 --- a/doc/Chamber-temperature.md +++ b/doc/Chamber-temperature.md @@ -21,7 +21,7 @@ Bellow is a reference configuration for Klipper. [heater_generic chamber_heater] heater_pin:PB10 max_power:1.0 -# Note: here the temperature sensor should be the sensor you are using for chamber temperature, not the PTC sensor +# Orca note: here the temperature sensor should be the sensor you are using for chamber temperature, not the PTC sensor sensor_type:NTC 100K MGB18-104F39050L32 sensor_pin:PA1 control = pid @@ -43,6 +43,8 @@ gcode: M117 Chamber heating cancelled {% else %} SET_HEATER_TEMPERATURE HEATER=chamber_heater TARGET={s} + # Orca: uncomment the following line if you want to use heat bed to assist chamber heating + # M140 S100 TEMPERATURE_WAIT SENSOR="heater_generic chamber_heater" MINIMUM={s-1} MAXIMUM={s+1} M117 Chamber at target temperature {% endif %} diff --git a/localization/i18n/it/OrcaSlicer_it.po b/localization/i18n/it/OrcaSlicer_it.po index 4a1d756596..e176e5ad4d 100644 --- a/localization/i18n/it/OrcaSlicer_it.po +++ b/localization/i18n/it/OrcaSlicer_it.po @@ -2,22 +2,22 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-10 14:54+0800\n" +"POT-Creation-Date: 2023-11-12 15:54+0800\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Localazy (https://localazy.com)\n" "Plural-Forms: nplurals=2; plural=(n==1) ? 0 : 1;\n" +"X-Generator: Poedit 3.4.1\n" msgid "Supports Painting" -msgstr "Support Painting" +msgstr "Pittura Supporti" msgid "Alt + Mouse wheel" msgstr "Alt + Rotella del mouse" msgid "Section view" -msgstr "Section view" +msgstr "Vista in sezione" msgid "Reset direction" msgstr "Ripristina direzione" @@ -26,7 +26,7 @@ msgid "Ctrl + Mouse wheel" msgstr "Ctrl + Rotella del mouse" msgid "Pen size" -msgstr "Pen size" +msgstr "Dimensione penna" msgid "Left mouse button" msgstr "Tasto sinistro mouse" @@ -44,13 +44,13 @@ msgid "Shift + Left mouse button" msgstr "Shift + Tasto sinistro mouse" msgid "Erase" -msgstr "Erase" +msgstr "Elimina" msgid "Erase all painting" -msgstr "Erase all painting" +msgstr "Cancellare tutta la pittura" msgid "Highlight overhang areas" -msgstr "Highlight overhangs" +msgstr "Evidenziare le sporgenze" msgid "Gap fill" msgstr "Riempimento gap" @@ -68,10 +68,10 @@ msgid "Smart fill angle" msgstr "Angolo riempimento intelligente" msgid "On overhangs only" -msgstr "Solo su sporgenze" +msgstr "Solo sulle sporgenze" msgid "Auto support threshold angle: " -msgstr "Auto support threshold angle: " +msgstr "Angolo di soglia per supporto automatico: " msgid "Circle" msgstr "Cerchio" @@ -80,7 +80,7 @@ msgid "Sphere" msgstr "Sfera" msgid "Fill" -msgstr "Fill" +msgstr "Riempi" msgid "Gap Fill" msgstr "Riempimento gap" @@ -90,42 +90,43 @@ msgid "Allows painting only on facets selected by: \"%1%\"" msgstr "Consente di pitturare solo sulle sfaccettature selezionate da: \"%1%\"" msgid "Highlight faces according to overhang angle." -msgstr "Highlight faces according to overhang angle." +msgstr "Evidenziare le facce in base all'angolo di sporgenza." msgid "No auto support" -msgstr "No auto support" +msgstr "Nessun supporto automatico" msgid "Support Generated" -msgstr "Support generated" +msgstr "Supporto generato" msgid "Lay on face" -msgstr "Lay on Face" +msgstr "Posiziona su faccia" #, boost-format msgid "" "Filament count exceeds the maximum number that painting tool supports. only " "the first %1% filaments will be available in painting tool." msgstr "" -"Filament count exceeds the maximum number that painting tool supports. Only " -"the first %1% filaments will be available in painting tool." +"Il numero di filamenti supera il numero massimo supportato dallo strumento " +"di pittura. Solo il primo %1% dei filamenti sarà disponibile nello strumento " +"di pittura." msgid "Color Painting" -msgstr "Color painting" +msgstr "Pittura a colori" msgid "Pen shape" -msgstr "Pen shape" +msgstr "Forma penna" msgid "Paint" -msgstr "Paint" +msgstr "Pittura" msgid "Key 1~9" -msgstr "Key 1~9" +msgstr "Tasto 1~9" msgid "Choose filament" -msgstr "Choose filament" +msgstr "Scegli filamento" msgid "Edge detection" -msgstr "Edge detection" +msgstr "Rilevamento dei bordi" msgid "Triangles" msgstr "Triangoli" @@ -143,7 +144,7 @@ msgid "Bucket fill" msgstr "Riempimento Secchio" msgid "Height range" -msgstr "Height range" +msgstr "Intervallo altezza" msgid "Ctrl + Shift + Enter" msgstr "Ctrl + Shift + Invio" @@ -155,23 +156,23 @@ msgid "Shortcut Key " msgstr "Shortcut Key " msgid "Triangle" -msgstr "Triangle" +msgstr "Triangolo" msgid "Height Range" -msgstr "Height Range" +msgstr "Interv. altezza" msgid "Vertical" -msgstr "" +msgstr "Verticale" msgid "Horizontal" -msgstr "" +msgstr "Orizzontale" msgid "Remove painted color" msgstr "Rimuovi colore dipinto" #, boost-format msgid "Painted using: Filament %1%" -msgstr "Painted using: Filament %1%" +msgstr "Pitturato utilizzando: Filamento %1%" msgid "Move" msgstr "Sposta" @@ -189,10 +190,10 @@ msgid "Scale" msgstr "Ridimensiona" msgid "Error: Please close all toolbar menus first" -msgstr "Error: Please close all toolbar menus first" +msgstr "Errore: chiudi prima tutti i menu della barra degli strumenti" msgid "Tool-Lay on Face" -msgstr "Tool-Lay on Face" +msgstr "Strumento-Faccia sul piatto" msgid "in" msgstr "in" @@ -207,19 +208,19 @@ msgid "Rotation" msgstr "Rotazione" msgid "Scale ratios" -msgstr "Scale ratios" +msgstr "Rapporti di scala" msgid "Object Operations" -msgstr "Object operations" +msgstr "Operazioni sugli oggetti" msgid "Volume Operations" -msgstr "Volume operations" +msgstr "Operazioni volume" msgid "Translate" msgstr "Traduci" msgid "Group Operations" -msgstr "Group operations" +msgstr "Operazioni Gruppo" msgid "Set Position" msgstr "Imposta posizione" @@ -231,7 +232,7 @@ msgid "Set Scale" msgstr "Imposta scala" msgid "Reset Position" -msgstr "Reset position" +msgstr "Ripristina posizione" msgid "Reset Rotation" msgstr "Reimposta rotazione" @@ -249,7 +250,7 @@ msgid "%" msgstr "%" msgid "uniform scale" -msgstr "Uniform scale" +msgstr "Scala uniforme" msgid "Left click" msgstr "Click sinistro" @@ -264,7 +265,7 @@ msgid "Remove connector" msgstr "Rimuovi connettore" msgid "Drag" -msgstr "Drag" +msgstr "Trascina" msgid "Move connector" msgstr "Sposta connettore" @@ -312,16 +313,16 @@ msgid "Place on cut" msgstr "Posiziona sul taglio" msgid "Flip" -msgstr "Flip" +msgstr "Ribalta" msgid "After cut" msgstr "Dopo il taglio" msgid "Cut to parts" -msgstr "Cut to parts" +msgstr "Taglia le parti" msgid "Auto Segment" -msgstr "Auto Segment" +msgstr "Segmento automatico" msgid "Perform cut" msgstr "Effettua taglio" @@ -333,7 +334,7 @@ msgid "Connectors" msgstr "Connettori" msgid "Type" -msgstr "Type" +msgstr "Tipo" msgid "Style" msgstr "Stile" @@ -351,7 +352,7 @@ msgid "Prizm" msgstr "Prizm" msgid "Frustum" -msgstr "Frustum" +msgstr "Tronco" msgid "Square" msgstr "Quadrato" @@ -416,8 +417,8 @@ msgid "" "Processing model '%1%' with more than 1M triangles could be slow. It is " "highly recommended to simplify the model." msgstr "" -"Processing model '%1%' with more than 1M triangles could be slow. It is " -"highly recommended to simplify the model." +"Lo slicing del modello \"%1%\" con più di 1 milione di triangoli potrebbe " +"essere lento. Si consiglia di semplificare il modello." msgid "Simplify model" msgstr "Semplifica modello" @@ -466,10 +467,10 @@ msgid "Operation already cancelling. Please wait few seconds." msgstr "Operazione già annullata. Si prega di attendere qualche secondo." msgid "Face recognition" -msgstr "Face recognition" +msgstr "Riconoscimento facciale" msgid "Perform Recognition" -msgstr "Perform Recognition" +msgstr "Esegui riconoscimento" msgid "Brush size" msgstr "Misura del pennello" @@ -535,16 +536,18 @@ msgstr "Indefinito" #, boost-format msgid "%1% was replaced with %2%" -msgstr "%1% was replaced with %2%" +msgstr "%1% è stato sostituito con %2%" msgid "The configuration may be generated by a newer version of OrcaSlicer." msgstr "" +"La configurazione potrebbe essere generata da una versione più recente di " +"OrcaSlicer." msgid "Some values have been replaced. Please check them:" -msgstr "Some values have been replaced. Please check them:" +msgstr "Alcuni valori sono stati sostituiti. Per favore controllali:" msgid "Process" -msgstr "Process" +msgstr "Processo" msgid "Filament" msgstr "Filamento" @@ -554,13 +557,15 @@ msgstr "Machine" msgid "Configuration package was loaded, but some values were not recognized." msgstr "" -"The configuration package was loaded, but some values were not recognized." +"Il pacchetto di configurazione è stato caricato, ma alcuni valori non sono " +"stati riconosciuti." #, boost-format msgid "" "Configuration file \"%1%\" was loaded, but some values were not recognized." msgstr "" -"The configuration file “%1%” was loaded, but some values were not recognized." +"Il file di configurazione \"%1%\" è stato caricato, ma alcuni valori non " +"sono stati riconosciuti." msgid "V" msgstr "V" @@ -569,6 +574,8 @@ msgid "" "OrcaSlicer will terminate because of running out of memory.It may be a bug. " "It will be appreciated if you report the issue to our team." msgstr "" +"OrcaSlicer ha esaurito la memoria e verrà chiuso. Questo potrebbe essere \n" +"un bug. Segnala questo errore al supporto tecnico.\"" msgid "Fatal error" msgstr "Errore irreversibile" @@ -577,19 +584,20 @@ msgid "" "OrcaSlicer will terminate because of a localization error. It will be " "appreciated if you report the specific scenario this issue happened." msgstr "" +"Si è verificato un errore nella localizzazione e OrcaSlicer verrà chiuso." msgid "Critical error" msgstr "Errore critico" #, boost-format msgid "OrcaSlicer got an unhandled exception: %1%" -msgstr "" +msgstr "OrcaSlicer ha ricevuto un'eccezione non gestita: %1%" msgid "Downloading Bambu Network Plug-in" msgstr "Scaricando il plug-in Bambu Network" msgid "Login information expired. Please login again." -msgstr "Login information expired. Please login again." +msgstr "Le informazioni di login sono scadute. Effettua nuovamente il login." msgid "Incorrect password" msgstr "Password errata" @@ -603,9 +611,11 @@ msgid "" "features.\n" "Click Yes to install it now." msgstr "" +"Orca Slicer richiede il runtime di Microsoft WebView2 per utilizzare " +"determinate funzionalità.\vFai clic su Sì per installarlo ora.\v" msgid "WebView2 Runtime" -msgstr "" +msgstr "WebView2 Runtime" #, c-format, boost-format msgid "" @@ -623,13 +633,13 @@ msgstr "Caricamento configurazione" #, c-format, boost-format msgid "Click to download new version in default browser: %s" -msgstr "Click to download new version in default browser: %s" +msgstr "Fai clic per scaricare la nuova versione nel browser predefinito: %s" msgid "The Orca Slicer needs an upgrade" -msgstr "Orca Slicer needs an update" +msgstr "Orca Slicer necessita di aggiornamento" msgid "This is the newest version." -msgstr "This is the newest version." +msgstr "Hai la versione più recente." msgid "Info" msgstr "Info" @@ -640,37 +650,42 @@ msgid "" "Please note, application settings will be lost, but printer profiles will " "not be affected." msgstr "" +"Il file di configurazione di OrcaSlicer potrebbe essere danneggiato e non " +"può essere analizzato.\n" +"OrcaSlicer ha tentato di ricreare il file di configurazione.\n" +"Si noti che le impostazioni dell'applicazione andranno perse, ma i profili " +"della stampante non saranno interessati." msgid "Rebuild" -msgstr "Rebuild" +msgstr "Ricrea" msgid "Loading current presets" -msgstr "Loading current presets" +msgstr "Caricamento dei preset correnti" msgid "Loading a mode view" -msgstr "Loading a mode view" +msgstr "Caricamento di una modalità di visualizzazione" msgid "Choose one file (3mf):" -msgstr "Choose one file (3mf):" +msgstr "Scegli file (3mf):" msgid "Choose one or more files (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" -msgstr "" +msgstr "Scegli uno o più file (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" msgid "Choose one or more files (3mf/step/stl/svg/obj/amf):" msgstr "Scegli uno o più file (3mf/step/stl/svg/obj/amf):" msgid "Choose one file (gcode/3mf):" -msgstr "" +msgstr "Scegli file (gcode/3mf):" msgid "Some presets are modified." -msgstr "Some presets are modified." +msgstr "Alcuni preset vengono modificati." msgid "" "You can keep the modifield presets to the new project, discard or save " "changes as new presets." msgstr "" -"You can keep the modified presets for the new project, discard, or save " -"changes as new presets." +"È possibile conservare i preset modificati per il nuovo progetto, eliminarli " +"o salvare le modifiche come nuovi preset." msgid "User logged out" msgstr "Utente disconnesso" @@ -679,14 +694,14 @@ msgid "new or open project file is not allowed during the slicing process!" msgstr "non è consentito aprire un nuovo file progetto durante lo slicing!" msgid "Open Project" -msgstr "Open Project" +msgstr "Apri Progetto" msgid "" "The version of Orca Slicer is too low and needs to be updated to the latest " "version before it can be used normally" msgstr "" -"The version of Orca Slicer is too low and needs to be updated to the latest " -"version before it can be used normally" +"La versione Orca Slicer è obsoleta, devi aggiornarla all'ultima versione \n" +"prima di poterla utilizzare normalmente" msgid "Privacy Policy Update" msgstr "Aggiornamento dell'informativa sulla privacy" @@ -695,10 +710,10 @@ msgid "Loading" msgstr "Caricamento" msgid "Loading user preset" -msgstr "Loading user preset" +msgstr "Caricamento del preset utente" msgid "Switching application language" -msgstr "Switching application language" +msgstr "Cambio lingua applicazione" msgid "Select the language" msgstr "Seleziona la lingua" @@ -719,7 +734,7 @@ msgid "Ongoing uploads" msgstr "Caricamenti in corso" msgid "Select a G-code file:" -msgstr "Select a G-code file:" +msgstr "Seleziona file G-code:" msgid "Import File" msgstr "Importa File…" @@ -728,56 +743,56 @@ msgid "Delete" msgstr "Elimina" msgid "Choose files" -msgstr "Choose files" +msgstr "Scegli file" msgid "New Folder" msgstr "Nuova cartella" msgid "Open" -msgstr "Open" +msgstr "Aperto" msgid "Rename" msgstr "Rinomina" msgid "Orca Slicer GUI initialization failed" -msgstr "Orca Slicer GUI initialization failed" +msgstr "Inizializzazione della GUI di Orca Slicer non riuscita" #, boost-format msgid "Fatal error, exception catched: %1%" -msgstr "Fatal error, eccezione registrata: %1%" +msgstr "Errore irreversibile, eccezione: %1%" msgid "Quality" msgstr "Qualità" msgid "Shell" -msgstr "Shell" +msgstr "Guscio" msgid "Infill" msgstr "Riempimento" msgid "Support" -msgstr "Support" +msgstr "Supporto" msgid "Flush options" -msgstr "Flush options" +msgstr "Opzioni spurgo" msgid "Speed" msgstr "Velocità" msgid "Strength" -msgstr "Strength" +msgstr "Resistenza" msgid "Top Solid Layers" -msgstr "Top solid layers" +msgstr "Strati solidi superiori" msgid "Top Minimum Shell Thickness" -msgstr "Top minimum shell thickness" +msgstr "Spessore minimo del guscio superiore" msgid "Bottom Solid Layers" -msgstr "Bottom solid layers" +msgstr "Layers solidi inferiori" msgid "Bottom Minimum Shell Thickness" -msgstr "Bottom minimum shell thickness" +msgstr "Spessore minimo del guscio inferiore" msgid "Ironing" msgstr "Stiratura" @@ -795,7 +810,7 @@ msgid "Wipe options" msgstr "Opzioni pulitura" msgid "Bed adhension" -msgstr "Bed adhesion" +msgstr "Adesione al piano" msgid "Advanced" msgstr "Avanzate" @@ -804,7 +819,7 @@ msgid "Add part" msgstr "Aggiungi parte" msgid "Add negative part" -msgstr "Add Negative Part" +msgstr "Aggiungi parte negativa" msgid "Add modifier" msgstr "Aggiungi modificatore" @@ -816,7 +831,7 @@ msgid "Add support enforcer" msgstr "Aggiungi rinforzo supporto" msgid "Select settings" -msgstr "Select settings" +msgstr "Seleziona impostazioni" msgid "Hide" msgstr "Nascondi" @@ -828,7 +843,7 @@ msgid "Del" msgstr "Del" msgid "Delete the selected object" -msgstr "Delete the selected object" +msgstr "Elimina l'oggetto selezionato" msgid "Edit Text" msgstr "Modifica testo" @@ -837,25 +852,25 @@ msgid "Load..." msgstr "Caricamento..." msgid "Orca Cube" -msgstr "" +msgstr "Orca Cube" msgid "3DBenchy" -msgstr "" +msgstr "3DBenchy" msgid "Autodesk FDM Test" -msgstr "" +msgstr "Autodesk FDM Test" msgid "Voron Cube" -msgstr "" +msgstr "Voron Cube" msgid "Cube" -msgstr "Cube" +msgstr "Cubo" msgid "Cylinder" msgstr "Cilindro" msgid "Cone" -msgstr "Cone" +msgstr "Cono" msgid "Height range Modifier" msgstr "Modifica intervallo di altezza" @@ -867,10 +882,10 @@ msgid "Change type" msgstr "Cambia tipo" msgid "Set as an individual object" -msgstr "Set as An Individual Object" +msgstr "Imposta come singolo oggetto" msgid "Set as individual objects" -msgstr "Set as Individual Objects" +msgstr "Imposta come singoli oggetti" msgid "Fill bed with copies" msgstr "Riempi il piano di copie" @@ -882,7 +897,7 @@ msgid "Printable" msgstr "Stampabile" msgid "Fix model" -msgstr "Fix Model" +msgstr "Correggi il modello" msgid "Export as STL" msgstr "Esporta come STL" @@ -900,98 +915,98 @@ msgid "Replace the selected part with new STL" msgstr "Sostituisci la parte selezionata con un nuovo STL" msgid "Change filament" -msgstr "Change filament" +msgstr "Cambia filamento" msgid "Set filament for selected items" -msgstr "Set filament for selected items" +msgstr "Imposta filamento per gli elementi selezionati" msgid "Default" msgstr "Predefinito" #, c-format, boost-format msgid "Filament %d" -msgstr "Filament %d" +msgstr "Filamento %d" msgid "active" msgstr "attivo" msgid "Scale to build volume" -msgstr "Scale to build volume" +msgstr "Scala per creare volume" msgid "Scale an object to fit the build volume" -msgstr "Scale an object to fit the build volume" +msgstr "Ridimensiona un oggetto per adattarlo al volume di costruzione" msgid "Flush Options" msgstr "Opzioni spurgo" msgid "Flush into objects' infill" -msgstr "Flush into objects' infill" +msgstr "Spurga nel riempimento oggetto" msgid "Flush into this object" -msgstr "Flush into this object" +msgstr "Spurga in questo oggetto" msgid "Flush into objects' support" -msgstr "Flush into objects' support" +msgstr "Spurga nei supporti dell'oggetto" msgid "Edit in Parameter Table" msgstr "Modifica nella tabella dei parametri" msgid "Convert from inch" -msgstr "Convert from Inches" +msgstr "Converti da pollici" msgid "Restore to inch" -msgstr "Restore to Inch" +msgstr "Ripristina in pollici" msgid "Convert from meter" -msgstr "Convert from Meters" +msgstr "Converti da metri" msgid "Restore to meter" -msgstr "Restore to Meter" +msgstr "Ripristina in metri" msgid "Assemble" -msgstr "Assemble" +msgstr "Assembla" msgid "Assemble the selected objects to an object with multiple parts" -msgstr "Assemble the selected objects into an object with multiple parts" +msgstr "Assembla gli oggetti selezionati in un oggetto con più parti" msgid "Assemble the selected objects to an object with single part" -msgstr "Assemble the selected objects into an object with single part" +msgstr "Assembla gli oggetti selezionati in un oggetto con una singola parte" msgid "Mesh boolean" -msgstr "" +msgstr "Mesh booleana" msgid "Mesh boolean operations including union and subtraction" -msgstr "" +msgstr "Operazioni booleane di mesh, tra cui l'unione e la sottrazione" msgid "Along X axis" msgstr "Lungo asse X" msgid "Mirror along the X axis" -msgstr "Mirror along the X Axis" +msgstr "Specchia lungo l'asse X" msgid "Along Y axis" msgstr "Lungo asse Y" msgid "Mirror along the Y axis" -msgstr "Mirror along the Y Axis" +msgstr "Specchia lungo l'asse Y" msgid "Along Z axis" msgstr "Lungo l'asse Z" msgid "Mirror along the Z axis" -msgstr "Mirror along the Z Axis" +msgstr "Specchia lungo l'asse Z" msgid "Mirror" msgstr "Specchia" msgid "Mirror object" -msgstr "Mirror object" +msgstr "Specchia Oggetto" msgid "Invalidate cut info" msgstr "Annulla informazioni di taglio" msgid "Add Primitive" -msgstr "Add Primitive" +msgstr "Aggiungi primitiva" msgid "Show Labels" msgstr "Mostra Etichette" @@ -1000,13 +1015,13 @@ msgid "To objects" msgstr "In oggetti" msgid "Split the selected object into multiple objects" -msgstr "Split the selected object into multiple objects" +msgstr "Dividi l'oggetto selezionato in più oggetti" msgid "To parts" msgstr "In parti" msgid "Split the selected object into multiple parts" -msgstr "Split the selected object into multiple parts" +msgstr "Dividi l'oggetto selezionato in più parti" msgid "Split" msgstr "Dividi" @@ -1015,49 +1030,49 @@ msgid "Split the selected object" msgstr "Dividi l'oggetto selezionato" msgid "Auto orientation" -msgstr "Auto orientation" +msgstr "Orientamento automatico" msgid "Auto orient the object to improve print quality." -msgstr "Auto orient the object to improve print quality." +msgstr "Orienta automaticamente l'oggetto per migliorare la qualità di stampa." msgid "Split the selected object into mutiple objects" -msgstr "Split the selected object into mutiple objects" +msgstr "Dividi l'oggetto selezionato in più oggetti" msgid "Split the selected object into mutiple parts" -msgstr "Split the selected object into mutiple parts" +msgstr "Dividi l'oggetto selezionato in più parti" msgid "Select All" -msgstr "Select All" +msgstr "Seleziona tutto" msgid "select all objects on current plate" -msgstr "Select all objects on the current plate" +msgstr "Seleziona tutti gli oggetti sul piatto corrente" msgid "Delete All" -msgstr "Delete All" +msgstr "Elimina tutto" msgid "delete all objects on current plate" -msgstr "Delete all objects on the current plate" +msgstr "Elimina tutti gli oggetti sul piatto corrente" msgid "Arrange" msgstr "Disponi" msgid "arrange current plate" -msgstr "Arrange current plate" +msgstr "Disponi sul piatto corrente" msgid "Auto Rotate" -msgstr "Auto Rotate" +msgstr "Rotazione automatica" msgid "auto rotate current plate" -msgstr "Auto rotate current plate" +msgstr "Rotazione automatica piatto corrente" msgid "Delete Plate" msgstr "Elimina piatto" msgid "Remove the selected plate" -msgstr "Remove the selected plate" +msgstr "Rimuovi il piatto selezionato" msgid "Clone" -msgstr "Clone" +msgstr "Clona" msgid "Simplify Model" msgstr "Semplifica Modello" @@ -1069,25 +1084,25 @@ msgid "Edit Process Settings" msgstr "Modifica le impostazioni del processo" msgid "Edit print parameters for a single object" -msgstr "Edit print parameters for a single object" +msgstr "Modifica i parametri di stampa per singolo oggetto" msgid "Change Filament" -msgstr "Change Filament" +msgstr "Cambia filamento" msgid "Set Filament for selected items" -msgstr "Set Filament for selected items" +msgstr "Imposta filamento per gli elementi selezionati" msgid "current" -msgstr "current" +msgstr "Attuale" msgid "Unlock" -msgstr "Unlock" +msgstr "Sblocca" msgid "Lock" -msgstr "Lock" +msgstr "Blocca" msgid "Edit Plate Name" -msgstr "" +msgstr "Modifica nome Piatto" msgid "Name" msgstr "Nome" @@ -1098,13 +1113,13 @@ msgstr "Fila." #, c-format, boost-format msgid "%1$d error repaired" msgid_plural "%1$d errors repaired" -msgstr[0] "%1$d error repaired" +msgstr[0] "%1$d Errore riparato" msgstr[1] "%1$d errors repaired" #, c-format, boost-format msgid "Error: %1$d non-manifold edge." msgid_plural "Error: %1$d non-manifold edges." -msgstr[0] "Error: %1$d non-manifold edge." +msgstr[0] "Errore: %1$d bordo non manifold." msgstr[1] "Error: %1$d non-manifold edges." msgid "Remaining errors" @@ -1113,29 +1128,37 @@ msgstr "Errori rimanenti" #, c-format, boost-format msgid "%1$d non-manifold edge" msgid_plural "%1$d non-manifold edges" -msgstr[0] "%1$d non-manifold edge" +msgstr[0] "%1$d bordo non-manifold" msgstr[1] "%1$d non-manifold edges" msgid "Right click the icon to fix model object" -msgstr "Right click the icon to fix model object" +msgstr "" +"Fai clic con il pulsante destro del mouse sull'icona per correggere " +"l'oggetto del modello" msgid "Right button click the icon to drop the object settings" -msgstr "Right click the icon to drop the object settings" +msgstr "" +"Fai clic con pulsante destro del mouse sull'icona per eliminare le " +"impostazioni dell'oggetto" msgid "Click the icon to reset all settings of the object" -msgstr "Click the icon to reset all settings of the object" +msgstr "Clicca sull'icona per ripristinare tutte le impostazioni dell'oggetto" msgid "Right button click the icon to drop the object printable property" -msgstr "Right click the icon to drop the object printable property" +msgstr "" +"Fai clic con pulsante destro del mouse sull'icona per eliminare le proprietà " +"stampa dell'oggetto" msgid "Click the icon to toggle printable property of the object" -msgstr "Click the icon to toggle printable properties of the object" +msgstr "" +"Clicca sull'icona per attivare o disattivare le proprietà stampabili " +"dell'oggetto" msgid "Click the icon to edit support painting of the object" -msgstr "Click the icon to edit support painting of the object" +msgstr "Clicca sull'icona per modificare la pittura del supporto dell'oggetto" msgid "Click the icon to edit color painting of the object" -msgstr "Click the icon to edit color painting for the object" +msgstr "Clicca sull'icona per modificare i colori dell'oggetto" msgid "Click the icon to shift this object to the bed" msgstr "Fare clic sull'icona per spostare l'oggetto sul piano" @@ -1147,16 +1170,18 @@ msgid "Error!" msgstr "Errore!" msgid "Failed to get the model data in the current file." -msgstr "" +msgstr "Impossibile ottenere i dati del modello nel file corrente." msgid "Generic" msgstr "Generico" msgid "Add Modifier" -msgstr "Add Modifier" +msgstr "Aggiungi modificatore" msgid "Switch to per-object setting mode to edit modifier settings." -msgstr "Switch to per-object setting mode to edit modifier settings." +msgstr "" +"Passa alla modalità di impostazione oggetto per modificare le impostazioni " +"del modificatore." msgid "" "Switch to per-object setting mode to edit process settings of selected " @@ -1198,13 +1223,14 @@ msgid "Delete all connectors" msgstr "Elimina tutti i connettori" msgid "Deleting the last solid part is not allowed." -msgstr "Deleting the last solid part is not allowed." +msgstr "Non è consentita l'eliminazione dell'ultima parte solida." msgid "The target object contains only one part and can not be splited." -msgstr "The target object contains only one part and cannot be split." +msgstr "" +"L'oggetto di destinazione contiene solo una parte e non può essere diviso." msgid "Assembly" -msgstr "Assembly" +msgstr "Assemblaggio" msgid "Cut Connectors information" msgstr "Informazioni sui connettori di taglio" @@ -1237,7 +1263,7 @@ msgid "Settings for height range" msgstr "Impostazioni intervallo altezza" msgid "Object" -msgstr "Object" +msgstr "Oggetto" msgid "Part" msgstr "Parte" @@ -1246,26 +1272,27 @@ msgid "Layer" msgstr "Layer" msgid "Selection conflicts" -msgstr "Selection conflicts" +msgstr "Conflitti di selezione" msgid "" "If first selected item is an object, the second one should also be object." msgstr "" -"If the first selected item is an object, the second one should also be an " -"object." +"Se il primo elemento selezionato è un oggetto, anche il secondo deve essere " +"un oggetto." msgid "" "If first selected item is a part, the second one should be part in the same " "object." msgstr "" -"If the first selected item is a part, the second one should be a part in the " -"same object." +"Se il primo elemento selezionato è una parte, il secondo deve far parte " +"dello stesso oggetto." msgid "The type of the last solid object part is not to be changed." -msgstr "The type of the last solid object part cannot be changed." +msgstr "" +"Il tipo dell'ultima parte dell'oggetto solido non può essere modificato." msgid "Negative Part" -msgstr "Negative Part" +msgstr "Parte negativa" msgid "Modifier" msgstr "Modificatore" @@ -1280,7 +1307,7 @@ msgid "Type:" msgstr "Tipo:" msgid "Choose part type" -msgstr "Choose part type" +msgstr "Scegli tipo di parte" msgid "Enter new name" msgstr "Inserisci un nuovo nome" @@ -1289,23 +1316,23 @@ msgid "Renaming" msgstr "Rinomina" msgid "Repairing model object" -msgstr "Repairing model object" +msgstr "Riparazione oggetto" msgid "Following model object has been repaired" msgid_plural "Following model objects have been repaired" -msgstr[0] "The following model object has been repaired" +msgstr[0] "Il seguente oggetto del modello è stato riparato" msgstr[1] "The following model objects have been repaired" msgid "Failed to repair folowing model object" msgid_plural "Failed to repair folowing model objects" -msgstr[0] "Failed to repair the following model object" +msgstr[0] "Impossibile riparare il seguente oggetto modello" msgstr[1] "Failed to repair the following model objects" msgid "Repairing was canceled" msgstr "La riparazione è stata annullata" msgid "Additional process preset" -msgstr "Additional process preset" +msgstr "Preset processo aggiuntivo" msgid "Remove parameter" msgstr "Rimuovi parametro" @@ -1320,28 +1347,29 @@ msgid "Add height range" msgstr "Aggiungi intervallo di altezza" msgid "Invalid numeric." -msgstr "Invalid numeric." +msgstr "Numero non valido." msgid "one cell can only be copied to one or multiple cells in the same column" -msgstr "One cell can only be copied to one or more cells in the same column." +msgstr "" +"Una cella può essere copiata solo in una o più celle della stessa colonna." msgid "multiple cells copy is not supported" -msgstr "Copying multiple cells is not supported." +msgstr "Copia di celle multiple non supportata." msgid "Outside" -msgstr "Outside" +msgstr "Esterno" msgid " " -msgstr "" +msgstr " " msgid "Layer height" msgstr "Altezza layer" msgid "Wall loops" -msgstr "Wall loops" +msgstr "Loop pareti" msgid "Infill density(%)" -msgstr "Infill density(%)" +msgstr "Densità riempimento (%)" msgid "Auto Brim" msgstr "Auto Brim" @@ -1350,7 +1378,7 @@ msgid "Auto" msgstr "Auto" msgid "Mouse ear" -msgstr "" +msgstr "Orecchio di topo" msgid "Outer brim only" msgstr "Solo brim esterno" @@ -1368,22 +1396,22 @@ msgid "Outer wall speed" msgstr "Velocità parete esterna" msgid "Plate" -msgstr "Plate" +msgstr "Piatto" msgid "Brim" msgstr "Brim" msgid "Object/Part Setting" -msgstr "Object/part setting" +msgstr "Impostazione oggetto/parte" msgid "Reset parameter" -msgstr "Reset parameter" +msgstr "Ripristina parametro" msgid "Multicolor Print" -msgstr "Multicolor Print" +msgstr "Stampa multicolore" msgid "Line Type" -msgstr "Line Type" +msgstr "Tipo linea" msgid "More" msgstr "Altro" @@ -1450,7 +1478,7 @@ msgid "Change filament at the beginning of this layer." msgstr "Cambia filamento all'inizio di questo layer." msgid "Delete Pause" -msgstr "" +msgstr "Elimina pausa" msgid "Delete Custom Template" msgstr "Elimina Template Personalizzato" @@ -1465,13 +1493,13 @@ msgid "Delete Filament Change" msgstr "Elimina cambio filamento" msgid "No printer" -msgstr "No printer" +msgstr "Nessuna stampante" msgid "..." -msgstr "" +msgstr "..." msgid "Failed to connect to the server" -msgstr "Failed to connect to the server" +msgstr "Connessione al server non riuscita" msgid "Check cloud service status" msgstr "Verifica lo stato del servizio cloud" @@ -1486,7 +1514,7 @@ msgid "Please click on the hyperlink above to view the cloud service status" msgstr "Fai clic sul link in alto per visualizzare lo stato del servizio cloud" msgid "Failed to connect to the printer" -msgstr "Failed to connect to the printer" +msgstr "Impossibile connettersi alla stampante" msgid "Connection to printer failed" msgstr "Connessione stampante fallita" @@ -1495,31 +1523,31 @@ msgid "Please check the network connection of the printer and Studio." msgstr "Controlla la connessione di rete della stampante e di Studio." msgid "Connecting..." -msgstr "Connecting..." +msgstr "Connessione..." msgid "?" msgstr "?" msgid "/" -msgstr "" +msgstr "/" msgid "Empty" -msgstr "Empty" +msgstr "Vuoto" msgid "AMS" msgstr "AMS" msgid "Auto Refill" -msgstr "" +msgstr "Ricarica automatica" msgid "AMS not connected" msgstr "AMS non collegato" msgid "Load Filament" -msgstr "Load" +msgstr "Carica" msgid "Unload Filament" -msgstr "Unload" +msgstr "Scarica Filamento" msgid "Ext Spool" msgstr "Bobina esterna" @@ -1534,43 +1562,45 @@ msgid "Retry" msgstr "Riprova" msgid "Calibrating AMS..." -msgstr "Calibrating AMS..." +msgstr "Calibrazione AMS..." msgid "A problem occured during calibration. Click to view the solution." -msgstr "A problem occured during calibration. Click to view the solution." +msgstr "" +"Si è verificato un problema durante la calibrazione. Clicca per visualizzare " +"la soluzione." msgid "Calibrate again" -msgstr "Calibrate again" +msgstr "Calibra di nuovo" msgid "Cancel calibration" -msgstr "Cancel calibration" +msgstr "Annulla calibrazione" msgid "Idling..." -msgstr "" +msgstr "Minimo..." msgid "Heat the nozzle" msgstr "Riscaldo nozzle" msgid "Cut filament" -msgstr "Cut filament" +msgstr "Taglio il filamento" msgid "Pull back current filament" -msgstr "Pull back the current filament" +msgstr "Ritiro il filamento corrente" msgid "Push new filament into extruder" msgstr "Inserisco il nuovo filamento nell'estrusore" msgid "Purge old filament" -msgstr "Purge old filament" +msgstr "Spurgo filamento precedente" msgid "Feed Filament" -msgstr "" +msgstr "Filamento di alimentazione" msgid "Confirm extruded" -msgstr "" +msgstr "Conferma estrusione" msgid "Check filament location" -msgstr "" +msgstr "Controllare la posizione del filamento" msgid "Grab new filament" msgstr "Prendo un nuovo filamento" @@ -1579,8 +1609,8 @@ msgid "" "Choose an AMS slot then press \"Load\" or \"Unload\" button to automatically " "load or unload filiament." msgstr "" -"Choose an AMS slot then press \"Load\" or \"Unload\" to automatically load " -"or unload filament." +"Seleziona uno slot AMS, premi \"Carica\" o \"Scarica\" per caricare o " +"scaricare automaticamente il filamento." msgid "Edit" msgstr "Modifica" @@ -1589,26 +1619,27 @@ msgid "" "All the selected objects are on the locked plate,\n" "We can not do auto-arrange on these objects." msgstr "" -"All the selected objects are on a locked plate.\n" -"Cannot auto-arrange these objects." +"Tutti gli oggetti selezionati si trovano su una piatto bloccato.\n" +"Non è possibile disporre automaticamente questi oggetti." msgid "No arrangable objects are selected." -msgstr "No arrangable objects are selected." +msgstr "Non sono stati selezionati oggetti ordinabili." msgid "" "This plate is locked,\n" "We can not do auto-arrange on this plate." msgstr "" -"This plate is locked.\n" -"We cannot auto-arrange this plate." +"Il piatto è bloccato.\n" +"Non puoi organizzare automaticamente questo piatto." msgid "Arranging..." -msgstr "Arranging..." +msgstr "Disponendo..." msgid "" "Arrange failed. Found some exceptions when processing object geometries." msgstr "" -"Arrange failed. Found some exceptions when processing object geometries." +"Disposizione fallita. Riscontrate eccezioni durante l'elaborazione delle " +"geometrie degli oggetti" msgid "Arranging" msgstr "Disposizione" @@ -1619,8 +1650,8 @@ msgstr "Disposizione annullata." msgid "" "Arranging is done but there are unpacked items. Reduce spacing and try again." msgstr "" -"Arranging complete, but some items were not able to be arranged. Reduce " -"spacing and try again." +"Disposizione completata, ma non è stato possibile disporre alcuni oggetti. " +"Ridurre lo spazio e riprovare." msgid "Arranging done." msgstr "Disposizione completata." @@ -1639,18 +1670,18 @@ msgid "" "All the selected objects are on the locked plate,\n" "We can not do auto-orient on these objects." msgstr "" -"All the selected objects are on a locked plate,\n" -"We cannot auto-orient these objects." +"Tutti gli oggetti selezionati si trovano su un piatto bloccato,\n" +"Non è possibile orientare automaticamente questi oggetti." msgid "" "This plate is locked,\n" "We can not do auto-orient on this plate." msgstr "" -"This plate is locked.\n" -"We cannot auto-orient this plate." +"Il piatto è bloccato.\n" +"Non puoi orientare automaticamente questo piatto." msgid "Orienting..." -msgstr "Orienting..." +msgstr "Orientamento..." msgid "Orienting" msgstr "Orientamento" @@ -1665,16 +1696,16 @@ msgid "Bed filling done." msgstr "Riempimento del piano completato." msgid "Error! Unable to create thread!" -msgstr "Error. Unable to create thread." +msgstr "Errore. Impossibile creare il processo." msgid "Exception" -msgstr "Exception" +msgstr "Eccezione" msgid "Logging in" -msgstr "Logging in" +msgstr "Accesso in corso..." msgid "Login failed" -msgstr "Login failed" +msgstr "Login non riuscito" msgid "Please check the printer network connection." msgstr "Controlla la connessione rete della stampante." @@ -1689,7 +1720,7 @@ msgid "Upload task timed out. Please check the network status and try again." msgstr "Attività di Upload scaduto. Controlla lo stato della rete e riprova." msgid "Cloud service connection failed. Please try again." -msgstr "Cloud service connection failed. Please try again." +msgstr "Connessione al servizio cloud non riuscita. Riprovare." msgid "Print file not found. please slice again." msgstr "File di stampa non trovato; si prega di eseguire nuovamente lo slice" @@ -1731,10 +1762,10 @@ msgstr "" "rete e riprova." msgid "Sending print job over LAN" -msgstr "Sending print job over LAN" +msgstr "Invia stampa tramite LAN" msgid "Sending print job through cloud service" -msgstr "Sending print job through cloud service" +msgstr "Invia stampa tramite servizio cloud" msgid "Service Unavailable" msgstr "Servizio non disponibile" @@ -1743,7 +1774,7 @@ msgid "Unkown Error." msgstr "Errore sconosciuto" msgid "Sending print configuration" -msgstr "Sending print configuration" +msgstr "Invia configurazione di stampa" #, c-format, boost-format msgid "Successfully sent. Will automatically jump to the device page in %ss" @@ -1753,6 +1784,7 @@ msgstr "" #, c-format, boost-format msgid "Successfully sent. Will automatically jump to the next page in %ss" msgstr "" +"Inviato con successo. Salterà automaticamente alla pagina successiva in %ss" msgid "An SD card needs to be inserted before printing via LAN." msgstr "" @@ -1855,10 +1887,10 @@ msgid "Copyright" msgstr "Copyright" msgid "License" -msgstr "License" +msgstr "Licenza" msgid "Orca Slicer is licensed under " -msgstr "Orca Slicer is licensed under " +msgstr "Orca Slicer è concesso in licenza con " msgid "GNU Affero General Public License, version 3" msgstr "GNU Affero General Public License, versione 3" @@ -1868,29 +1900,32 @@ msgid "" "by Prusa Research. PrusaSlicer is from Slic3r by Alessandro Ranellucci and " "the RepRap community" msgstr "" +"Orca Slicer è basato su BambuStudio di Bambulab, che è di PrusaSlicer di " +"Prusa Research. PrusaSlicer è di Slic3r di Alessandro Ranellucci e della " +"comunità RepRap" msgid "Libraries" -msgstr "Libraries" +msgstr "Librerie" msgid "" "This software uses open source components whose copyright and other " "proprietary rights belong to their respective owners" msgstr "" -"This software uses open source components whose copyright and other " -"proprietary rights belong to their respective owners" +"Questo software utilizza componenti open source il cui copyright e altri " +"diritti di proprietà appartengono ai rispettivi proprietari" #, c-format, boost-format msgid "About %s" msgstr "Informazioni su %s" msgid "Orca Slicer " -msgstr "" +msgstr "Orca Slicer " msgid "OrcaSlicer is based on BambuStudio, PrusaSlicer, and SuperSlicer." -msgstr "" +msgstr "OrcaSlicer è basato su BambuStudio, PrusaSlicer e SuperSlicer." msgid "BambuStudio is originally based on PrusaSlicer by PrusaResearch." -msgstr "" +msgstr "BambuStudio è originariamente basato su PrusaSlicer di PrusaResearch." msgid "PrusaSlicer is originally based on Slic3r by Alessandro Ranellucci." msgstr "PrusaSlicer originariamente basato su Slic3r di Alessandro Ranellucci." @@ -1909,20 +1944,20 @@ msgid "AMS Materials Setting" msgstr "Impostazione materiali AMS" msgid "Confirm" -msgstr "Confirm" +msgstr "Conferma" msgid "Close" msgstr "Chiudi" msgid "Colour" -msgstr "Color" +msgstr "Colore" msgid "" "Nozzle\n" "Temperature" msgstr "" -"Nozzle\n" -"Temperature" +"Temperatura\n" +"Nozzle" msgid "max" msgstr "max" @@ -1932,7 +1967,7 @@ msgstr "min" #, boost-format msgid "The input value should be greater than %1% and less than %2%" -msgstr "The input value should be greater than %1% and less than %2%" +msgstr "Il valore di input deve essere maggiore di %1% e minore di %2%" msgid "SN" msgstr "SN" @@ -1943,10 +1978,10 @@ msgstr "" "supportata." msgid "Factors of Flow Dynamics Calibration" -msgstr "" +msgstr "Fattori di calibrazione della dinamica del flusso" msgid "PA Profile" -msgstr "" +msgstr "Profilo PA" msgid "Factor K" msgstr "Fattore K" @@ -1997,7 +2032,7 @@ msgid "Bed Type" msgstr "Tipo di piano" msgid "Nozzle temperature" -msgstr "Temperatura ugello" +msgstr "Temperatura Nozzle" msgid "Bed Temperature" msgstr "Temperatura piano" @@ -2006,7 +2041,7 @@ msgid "Max volumetric speed" msgstr "Massima velocità volumetrica" msgid "℃" -msgstr "" +msgstr "°C" msgid "Bed temperature" msgstr "Temperatura piano" @@ -2140,16 +2175,20 @@ msgid "" "When the current material run out, the printer will continue to print in the " "following order." msgstr "" +"Quando si esaurisce il materiale corrente, la stampante continuerà a " +"stampare nel seguente ordine." msgid "Group" msgstr "Gruppo" msgid "The printer does not currently support auto refill." -msgstr "" +msgstr "La stampante non supporta attualmente la ricarica automatica." msgid "" "AMS filament backup is not enabled, please enable it in the AMS settings." msgstr "" +"Il backup del filamento AMS non è abilitato, si prega di abilitarlo nelle " +"impostazioni AMS." msgid "" "If there are two identical filaments in AMS, AMS filament backup will be " @@ -2157,54 +2196,59 @@ msgid "" "(Currently supporting automatic supply of consumables with the same brand, " "material type, and color)" msgstr "" +"Se ci sono due filamenti identici in AMS, il backup del filamento AMS sarà " +"abilitato.\n" +"(Attualmente supporta la fornitura automatica di materiali di consumo con lo " +"stesso marchio, tipo di materiale e colore)" msgid "AMS Settings" -msgstr "AMS Settings" +msgstr "Impostazioni AMS" msgid "Insertion update" -msgstr "Insertion update" +msgstr "Aggiornamento dell'inserimento" msgid "" "The AMS will automatically read the filament information when inserting a " "new Bambu Lab filament. This takes about 20 seconds." msgstr "" -"The AMS will automatically read the filament information when inserting a " -"new Bambu Lab filament spool. This takes about 20 seconds." +"L'AMS leggerà automaticamente le informazioni sul filamento quando inserisce " +"una nuova bobina di filamento Bambu Lab. Questa operazione richiede circa 20 " +"secondi." msgid "" "Note: if new filament is inserted during printing, the AMS will not " "automatically read any information until printing is completed." msgstr "" -"Note: if new filament is inserted during printing, the AMS will not " -"automatically read any information until printing has finished." +"Nota: se durante la stampa viene inserito un nuovo filamento, l'AMS non " +"leggerà automaticamente alcuna informazione fino al termine della stampa." msgid "" "When inserting a new filament, the AMS will not automatically read its " "information, leaving it blank for you to enter manually." msgstr "" -"When inserting a new filament, the AMS will not automatically read its " -"information, leaving it blank for you to enter manually." +"Quando si inserisce un nuovo filamento, l'AMS non legge automaticamente le " +"sue informazioni, lasciandole vuote per l'inserimento manuale." msgid "Power on update" -msgstr "Update on startup" +msgstr "Aggiorna all'avvio" msgid "" "The AMS will automatically read the information of inserted filament on " "start-up. It will take about 1 minute.The reading process will roll filament " "spools." msgstr "" -"The AMS will automatically read the information of inserted filament on " -"start-up. It will take about 1 minute.The reading process will rotate the " -"filament spools." +"L'AMS leggerà automaticamente le informazioni sul filamento inserito " +"all'avvio. Ci vorrà circa 1 minuto. Il processo di lettura farà ruotare le " +"bobine del filamento." msgid "" "The AMS will not automatically read information from inserted filament " "during startup and will continue to use the information recorded before the " "last shutdown." msgstr "" -"The AMS will not automatically read information from inserted filament " -"during startup and will continue to use the information recorded before the " -"last shutdown." +"L'AMS non leggerà automaticamente le informazioni dal filamento inserito " +"durante l'avvio e continuerà a utilizzare le informazioni registrate prima " +"dell'ultimo spegnimento." msgid "Update remaining capacity" msgstr "Aggiorna capacità residua" @@ -2232,7 +2276,7 @@ msgid "File" msgstr "File" msgid "Calibration" -msgstr "Calibration" +msgstr "Calibrazione" msgid "" "Failed to download the plug-in. Please check your firewall settings and vpn " @@ -2269,14 +2313,14 @@ msgid "" "A error occurred. Maybe memory of system is not enough or it's a bug of the " "program" msgstr "" -"An error occurred. The system may have run out of memory, or a bug may have " -"occurred." +"Si è verificato un errore. È possibile che la memoria del sistema sia " +"esaurita o che si sia verificato un bug." msgid "Please save project and restart the program. " -msgstr "Please save your project and restart the application." +msgstr "Salva il progetto e riavvia l'applicazione." msgid "Processing G-Code from Previous file..." -msgstr "Processing G-Code from previous file…" +msgstr "Elaborazione G-Code dal file precedente..." msgid "Slicing complete" msgstr "Slicing completato" @@ -2291,10 +2335,10 @@ msgid "Divide by zero" msgstr "Dividi per zero" msgid "Overflow" -msgstr "Overflow" +msgstr "Sovrafflusso" msgid "Underflow" -msgstr "Underflow" +msgstr "Sotto-estruso" msgid "Floating reserved operand" msgstr "Floating reserved operand" @@ -2303,7 +2347,7 @@ msgid "Stack overflow" msgstr "Stack overflow" msgid "Unknown error when export G-code." -msgstr "Unknown error with G-code export" +msgstr "Errore sconosciuto nell'esportazione G-code" #, boost-format msgid "" @@ -2311,13 +2355,13 @@ msgid "" "Error message: %1%.\n" "Source file %2%." msgstr "" -"Failed to save G-code file.\n" -"Error message: %1%.\n" -"Source file %2%." +"Impossibile salvare il file del G-code.\n" +"Messaggio di errore: %1%.\n" +"File sorgente %2%." #, boost-format msgid "Succeed to export G-code to %1%" -msgstr "Success! G-code exported to %1%" +msgstr "Successo! G-code esportato in %1%" msgid "Running post-processing scripts" msgstr "Esecuzione script di post-elaborazione" @@ -2328,8 +2372,8 @@ msgstr "Copia del G-code temporaneo nel G-code di output non riuscita." #, boost-format msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" msgstr "" -"Pianificazione caricamento su `%1% `. Vedi Finestra -> Coda di caricamento " -"dell'host di stampa" +"Programmazione del caricamento su `%1%`. Vedere finestra -> Coda di " +"caricamento Host di Stampa" msgid "Origin" msgstr "Origine" @@ -2423,8 +2467,8 @@ msgid "" "Recommended nozzle temperature of this filament type is [%d, %d] degree " "centigrade" msgstr "" -"The recommended nozzle temperature for this filament type is [%d, %d] " -"degrees centigrade" +"La temperatura del nozzle consigliata per questo filamento è [%d, %d] gradi " +"centigradi" msgid "" "Too small max volumetric speed.\n" @@ -2439,29 +2483,32 @@ msgid "" "it may result in material softening and clogging.The maximum safe " "temperature for the material is %d" msgstr "" +"L'attuale temperatura della camera è superiore alla temperatura di sicurezza " +"del materiale, può causare l'ammorbidimento e l'intasamento del materiale. " +"La temperatura massima di sicurezza per il materiale è %d" msgid "" "Too small layer height.\n" "Reset to 0.2" msgstr "" -"Layer height too small\n" -"It has been reset to 0.2" +"Altezza del layer troppo piccola\n" +"È stata ripristinata a 0,2" msgid "" "Too small ironing spacing.\n" "Reset to 0.1" msgstr "" -"Ironing spacing too small\n" -"It has been reset to 0.1" +"Spaziatura stiratura troppo piccola\n" +"È stata ripristinata a 0,1" msgid "" "Zero initial layer height is invalid.\n" "\n" "The first layer height will be reset to 0.2." msgstr "" -"Zero first layer height is invalid.\n" +"L'altezza zero primo layer non è valida.\n" "\n" -"The first layer height will be reset to 0.2." +"L'altezza primo layer verrà ripristinata a 0,2." msgid "" "This setting is only used for model size tunning with small value in some " @@ -2471,11 +2518,13 @@ msgid "" "\n" "The value will be reset to 0." msgstr "" -"This setting is only used for tuning model size by small amounts.\n" -"For example, when the model size has small errors or when tolerances are " -"incorrect. For large adjustments, please use the model scale function.\n" +"Questa viene utilizzata solo per regolare le dimensioni del modello in " +"piccole quantità.\n" +"Ad esempio, quando le dimensioni del modello presentano piccoli errori o " +"quando le tolleranze non sono corrette. Per regolazioni di grandi " +"dimensioni, si prega di utilizzare la funzione di scala del modello.\n" "\n" -"The value will be reset to 0." +"Il valore verrà reimpostato su 0." msgid "" "Too large elefant foot compensation is unreasonable.\n" @@ -2484,11 +2533,12 @@ msgid "" "\n" "The value will be reset to 0." msgstr "" -"The elephant foot compensation value is too large.\n" -"If there are significant elephant foot issues, please check other settings.\n" -"The bed temperature may be too high, for example.\n" +"Il valore di compensazione del piede di elefante è troppo grande.\n" +"Se i problemi zampa d'elefante sono significativi, controllare altre " +"impostazioni.\n" +"Ad esempio, la temperatura del piano potrebbe essere troppo alta.\n" "\n" -"The value will be reset to 0." +"Il valore viene riportato a 0." msgid "" "Spiral mode only works when wall loops is 1, support is disabled, top shell " @@ -2499,16 +2549,16 @@ msgstr "" "0 e il tipo di timelapse è tradizionale" msgid " But machines with I3 structure will not generate timelapse videos." -msgstr "" +msgstr " Ma le macchine con la struttura I3 non genereranno video timelapse." msgid "" "Change these settings automatically? \n" "Yes - Change these settings and enable spiral mode automatically\n" "No - Give up using spiral mode this time" msgstr "" -"Change these settings automatically? \n" -"Yes - Change these settings and enable spiral/vase mode automatically\n" -"No - Cancel enabling spiral mode" +"Modificare queste impostazioni automaticamente? \n" +"Si - Modifica queste impostazioni ed abilita la modalità spirale/vaso\n" +"No - Annulla l'attivazione della modalità a spirale" msgid "" "Prime tower does not work when Adaptive Layer Height or Independent Support " @@ -2517,11 +2567,11 @@ msgid "" "YES - Keep Prime Tower\n" "NO - Keep Adaptive Layer Height and Independent Support Layer Height" msgstr "" -"Prime tower does not work when Adaptive Layer Height or Independent Support " -"Layer Height is on.\n" -"Which do you want to keep?\n" -"YES - Keep Prime Tower\n" -"NO - Keep Adaptive Layer Height and Independent Support Layer Height" +"Prime tower non funziona quando layer Adattativo o Altezza supporto Layer " +"indipendente sono attivati.\n" +"Quale vuoi tenere?\n" +"SÌ - Mantieni Prime Tower\n" +"NO - Mantieni layer Adattativo e Altezza supporto Layer indipendente" msgid "" "Prime tower does not work when Adaptive Layer Height is on.\n" @@ -2529,10 +2579,10 @@ msgid "" "YES - Keep Prime Tower\n" "NO - Keep Adaptive Layer Height" msgstr "" -"Prime tower does not work when Adaptive Layer Height is on.\n" -"Which do you want to keep?\n" -"YES - Keep Prime Tower\n" -"NO - Keep Adaptive Layer Height" +"Prime tower non funziona quando layer adattativo è attivo.\n" +"Quale vuoi tenere?\n" +"SÌ - Mantieni Prime Tower\n" +"NO - Mantieni l'ayer adattativo" msgid "" "Prime tower does not work when Independent Support Layer Height is on.\n" @@ -2540,14 +2590,15 @@ msgid "" "YES - Keep Prime Tower\n" "NO - Keep Independent Support Layer Height" msgstr "" -"Prime tower does not work when Independent Support Layer Height is on.\n" -"Which do you want to keep?\n" -"YES - Keep Prime Tower\n" -"NO - Keep Independent Support Layer Height" +"Prime tower non funziona quando Altezza Supporto Layer indipendente è " +"attiva.\n" +"Quale vuoi tenere?\n" +"SÌ - Mantieni Prime Tower\n" +"NO - Mantieni Altezza Supporto Layer indipendente" #, boost-format msgid "%1% infill pattern doesn't support 100%% density." -msgstr "%1% infill pattern doesn't support 100%% density." +msgstr "La trama riempimento %1% non supporta il 100%% di densità." msgid "" "Switch to rectilinear pattern?\n" @@ -2569,94 +2620,94 @@ msgid "Auto bed leveling" msgstr "Auto bed leveling" msgid "Heatbed preheating" -msgstr "Heatbed preheating" +msgstr "Preriscaldamento del piano" msgid "Sweeping XY mech mode" -msgstr "Sweeping XY mech mode" +msgstr "Modalità Sweeping XY mech" msgid "Changing filament" -msgstr "Changing filament" +msgstr "Cambio filamento" msgid "M400 pause" msgstr "M400 pause" msgid "Paused due to filament runout" -msgstr "Paused due to filament runout" +msgstr "Pausa per filamento esaurito" msgid "Heating hotend" -msgstr "Heating hotend" +msgstr "Riscaldamento hotend" msgid "Calibrating extrusion" -msgstr "Calibrating extrusion" +msgstr "Calibrazione estrusione" msgid "Scanning bed surface" -msgstr "Scanning bed surface" +msgstr "Scansione superfice piatto" msgid "Inspecting first layer" -msgstr "Inspecting first layer" +msgstr "Ispezione del primo layer" msgid "Identifying build plate type" -msgstr "Identifying build plate type" +msgstr "Identificazione tipo piatto di costruzione" msgid "Calibrating Micro Lidar" -msgstr "Calibrating Micro Lidar" +msgstr "Calibrazione Micro Lidar" msgid "Homing toolhead" -msgstr "Homing toolhead" +msgstr "Homing testa di stampa" msgid "Cleaning nozzle tip" -msgstr "Cleaning nozzle tip" +msgstr "Pulizia nozzle" msgid "Checking extruder temperature" -msgstr "Checking extruder temperature" +msgstr "Controllo temperatura dell'estrusore" msgid "Printing was paused by the user" -msgstr "Printing was paused by the user" +msgstr "Stampa messa in pausa dall'utente" msgid "Pause of front cover falling" -msgstr "Pause of front cover falling" +msgstr "Pausa caduta cover anteriore" msgid "Calibrating the micro lida" -msgstr "Calibrating the micro lidar" +msgstr "Calibrazione micro lidar" msgid "Calibrating extrusion flow" -msgstr "Calibrating extrusion flow" +msgstr "Calibrazione flusso estrusore" msgid "Paused due to nozzle temperature malfunction" -msgstr "Paused due to nozzle temperature malfunction" +msgstr "Pausa per malfunzionamento temperatura nozzle" msgid "Paused due to heat bed temperature malfunction" -msgstr "Paused due to heat bed temperature malfunction" +msgstr "Pausa per malfunzionamento della temperatura piano termico" msgid "Filament unloading" -msgstr "" +msgstr "Scarico del filamento" msgid "Skip step pause" -msgstr "" +msgstr "Salta la pausa del passaggio" msgid "Filament loading" -msgstr "" +msgstr "Caricamento del filamento" msgid "Motor noise calibration" -msgstr "" +msgstr "Calibrazione del rumore del motore" msgid "Paused due to AMS lost" -msgstr "" +msgstr "Sospeso a causa della perdita di AMS" msgid "Paused due to low speed of the heat break fan" -msgstr "" +msgstr "Pausa a causa della bassa velocità della ventola a taglio termico" msgid "Paused due to chamber temperature control error" -msgstr "" +msgstr "Pausa a causa di un errore di controllo della temperatura della camera" msgid "Cooling chamber" -msgstr "" +msgstr "Camera di raffreddamento" msgid "Paused by the Gcode inserted by user" -msgstr "" +msgstr "Messo in pausa dal Gcode inserito dall'utente" msgid "Motor noise showoff" -msgstr "" +msgstr "Messa in mostra del rumore del motore" msgid "MC" msgstr "MC" @@ -2674,43 +2725,52 @@ msgid "Unknown" msgstr "Sconosciuto" msgid "Fatal" -msgstr "Fatal" +msgstr "Irreversibile" msgid "Serious" -msgstr "Serious" +msgstr "Serio" msgid "Common" -msgstr "Common" +msgstr "Comune" msgid "Update successful." -msgstr "Update successful." +msgstr "Aggiornamento riuscito." msgid "Downloading failed." msgstr "Downloading failed." msgid "Verification failed." -msgstr "Verification failed." +msgstr "Verifica fallita." msgid "Update failed." -msgstr "Update failed." +msgstr "Aggiornamento fallito" msgid "" "The current chamber temperature or the target chamber temperature exceeds " "45℃.In order to avoid extruder clogging,low temperature filament(PLA/PETG/" "TPU) is not allowed to be loaded." msgstr "" +"La temperatura attuale della camera o la temperatura della camera target " +"supera i 45°C. Al fine di evitare l'intasamento dell'estrusore, il filamento " +"a bassa temperatura (PLA/PETG/TPU) non può essere caricato." msgid "" "Low temperature filament(PLA/PETG/TPU) is loaded in the extruder.In order to " "avoid extruder clogging,it is not allowed to set the chamber temperature " "above 45℃." msgstr "" +"Il filamento a bassa temperatura (PLA/PETG/TPU) viene caricato " +"nell'estrusore. Al fine di evitare l'intasamento dell'estrusore, non è " +"consentito impostare la temperatura della camera al di sopra di 45°C." msgid "" "When you set the chamber temperature below 40℃, the chamber temperature " "control will not be activated. And the target chamber temperature will " "automatically be set to 0℃." msgstr "" +"Quando si imposta la temperatura della camera al di sotto di 40°C, il " +"controllo della temperatura della camera non verrà attivato. E la " +"temperatura della camera target verrà impostata automaticamente su 0°C." msgid "Failed to start printing job" msgstr "Impossibile avviare il processo di stampa" @@ -2718,34 +2778,40 @@ msgstr "Impossibile avviare il processo di stampa" msgid "" "This calibration does not support the currently selected nozzle diameter" msgstr "" +"Questa calibrazione non supporta il diametro dell'ugello attualmente " +"selezionato" msgid "Current flowrate cali param is invalid" -msgstr "" +msgstr "Il parametro cali della portata corrente non è valido" msgid "Selected diameter and machine diameter do not match" -msgstr "" +msgstr "Il diametro selezionato e il diametro della macchina non corrispondono" msgid "Failed to generate cali gcode" -msgstr "" +msgstr "Impossibile generare cali gcode" msgid "Calibration error" -msgstr "" +msgstr "Errore di calibrazione" msgid "TPU is not supported by AMS." -msgstr "" +msgstr "Il TPU non è supportato da AMS." msgid "Bambu PET-CF/PA6-CF is not supported by AMS." -msgstr "" +msgstr "Bambu PET-CF/PA6-CF non è supportato da AMS." msgid "" "Damp PVA will become flexible and get stuck inside AMS,please take care to " "dry it before use." msgstr "" +"Damp PVA diventerà flessibile e rimarrà bloccato all'interno di AMS, si " +"prega di fare attenzione ad asciugarlo prima dell'uso." msgid "" "CF/GF filaments are hard and brittle, It's easy to break or get stuck in " "AMS, please use with caution." msgstr "" +"I filamenti CF / GF sono duri e fragili, è facile rompersi o rimanere " +"bloccati in AMS, si prega di usare con cautela." msgid "default" msgstr "predefinito" @@ -2758,17 +2824,17 @@ msgstr "N/A" #, c-format, boost-format msgid "%s can't be percentage" -msgstr "%s can’t be a percentage" +msgstr "%s non può essere una percentuale" #, c-format, boost-format msgid "Value %s is out of range, continue?" -msgstr "Value %s is out of range, continue?" +msgstr "Il valore %s non rientra nell'intervallo, continuare?" msgid "Parameter validation" msgstr "Validazione parametri" msgid "Value is out of range." -msgstr "Value is out of range." +msgstr "Valore fuori intervallo." #, c-format, boost-format msgid "" @@ -2782,16 +2848,16 @@ msgstr "" #, boost-format msgid "Invalid format. Expected vector format: \"%1%\"" -msgstr "Invalid format. Expected vector format: \"%1%\"" +msgstr "Formato non valido. Formato vettoriale previsto: \"%1%\"" msgid "Layer Height" -msgstr "Layer Height" +msgstr "Altezza layer" msgid "Line Width" -msgstr "Line Width" +msgstr "Larghezza linea" msgid "Fan Speed" -msgstr "Fan Speed" +msgstr "Velocità ventola" msgid "Temperature" msgstr "Temperatura" @@ -2830,13 +2896,13 @@ msgid "Temperature: " msgstr "Temperatura:" msgid "Loading G-codes" -msgstr "Loading G-code" +msgstr "Caricamento del G-code" msgid "Generating geometry vertex data" -msgstr "Generating geometry vertex data" +msgstr "Generazione dati vertici geometria" msgid "Generating geometry index data" -msgstr "Generating geometry index data" +msgstr "Generazione dati di indice geometrico" msgid "Statistics of All Plates" msgstr "Statistiche di tutti i piatti" @@ -2851,13 +2917,13 @@ msgid "Total" msgstr "Totale" msgid "Total Estimation" -msgstr "Total estimation" +msgstr "Stima totale" msgid "Total time" msgstr "Tempo totale" msgid "Total cost" -msgstr "" +msgstr "Costo totale" msgid "up to" msgstr "fino a" @@ -2869,19 +2935,19 @@ msgid "from" msgstr "da" msgid "Color Scheme" -msgstr "Color scheme" +msgstr "Schema Colore" msgid "Time" msgstr "Tempo" msgid "Percent" -msgstr "Percent" +msgstr "Percentuale" msgid "Layer Height (mm)" -msgstr "Layer height (mm)" +msgstr "Altezza layer (mm)" msgid "Line Width (mm)" -msgstr "Line width (mm)" +msgstr "Larghezza linea (mm)" msgid "Speed (mm/s)" msgstr "Velocità (mm/s)" @@ -2902,16 +2968,16 @@ msgid "Travel" msgstr "Spostamento" msgid "Seams" -msgstr "Giunzioni" +msgstr "Cuciture" msgid "Retract" -msgstr "Retract" +msgstr "Retrazione" msgid "Unretract" -msgstr "Unretract" +msgstr "De-retrazione" msgid "Filament Changes" -msgstr "Filament changes" +msgstr "Cambi filamento" msgid "Wipe" msgstr "Pulitura" @@ -2920,13 +2986,13 @@ msgid "Options" msgstr "Opzioni" msgid "travel" -msgstr "Travel" +msgstr "Spostamento" msgid "Extruder" msgstr "Estrusore" msgid "Filament change times" -msgstr "Filament change times" +msgstr "Tempi cambio filamento" msgid "Cost" msgstr "Costo" @@ -2953,16 +3019,16 @@ msgid "Normal mode" msgstr "Modalità normale" msgid "Prepare time" -msgstr "Prepare time" +msgstr "Tempo preparazione" msgid "Model printing time" -msgstr "Model printing time" +msgstr "Tempo stampa del modello" msgid "Switch to silent mode" -msgstr "Switch to silent mode" +msgstr "Passa a modalità silenziosa" msgid "Switch to normal mode" -msgstr "Switch to normal mode" +msgstr "Passa a modalità normale" msgid "Variable layer height" msgstr "Altezza layer adattativo" @@ -2974,7 +3040,7 @@ msgid "Quality / Speed" msgstr "Qualità / Velocità" msgid "Smooth" -msgstr "Smooth" +msgstr "Leviga" msgid "Radius" msgstr "Raggio" @@ -3013,13 +3079,13 @@ msgid "Increase/decrease edit area" msgstr "Aumenta/diminuisci l'area di modifica" msgid "Sequence" -msgstr "Sequence" +msgstr "Sequenza" msgid "Mirror Object" msgstr "Specchia Oggetto" msgid "Tool Move" -msgstr "Tool move" +msgstr "Sposta strumento" msgid "Tool Rotate" msgstr "Strumento Ruota" @@ -3028,16 +3094,16 @@ msgid "Move Object" msgstr "Sposta oggetto" msgid "Auto Orientation options" -msgstr "Auto orientation options" +msgstr "Opzioni orientamento automatico" msgid "Enable rotation" -msgstr "Enable rotation" +msgstr "Abilita rotazione" msgid "Optimize support interface area" -msgstr "Optimize support interface area" +msgstr "Ottimizza l'area dell'interfaccia di supporto" msgid "Orient" -msgstr "Orient" +msgstr "Orienta" msgid "Arrange options" msgstr "Opzioni di disposizione" @@ -3046,31 +3112,31 @@ msgid "Spacing" msgstr "Spaziatura " msgid "Auto rotate for arrangement" -msgstr "Auto rotate for arrangement" +msgstr "Ruota automaticamente per disporre" msgid "Allow multiple materials on same plate" -msgstr "Allow multiple materials on same plate" +msgstr "Consenti più materiali sullo stesso piatto" msgid "Avoid extrusion calibration region" -msgstr "Avoid extrusion calibration region" +msgstr "Evitare la regione di calibrazione dell'estrusione" msgid "Align to Y axis" -msgstr "" +msgstr "Allinea all'asse Y" msgid "Add" msgstr "Aggiungi" msgid "Add plate" -msgstr "Add Plate" +msgstr "Aggiungi piatto" msgid "Auto orient" -msgstr "Auto Orient" +msgstr "Orientamento automatico" msgid "Arrange all objects" -msgstr "Arrange all objects" +msgstr "Disponi tutti gli oggetti" msgid "Arrange objects on selected plates" -msgstr "Arrange objects on selected plates" +msgstr "Disponi gli oggetti sui piatti selezionati" msgid "Split to objects" msgstr "Dividi in oggetti" @@ -3079,22 +3145,22 @@ msgid "Split to parts" msgstr "Dividi in parti" msgid "Assembly View" -msgstr "Assembly View" +msgstr "Vista montaggio" msgid "Select Plate" -msgstr "Select Plate" +msgstr "Seleziona piatto" msgid "Assembly Return" -msgstr "Assembly Return" +msgstr "Ritorna al montaggio" msgid "return" -msgstr "return" +msgstr "Indietro" msgid "Paint Toolbar" -msgstr "Paint Toolbar" +msgstr "Barra strumenti di pittura" msgid "Explosion Ratio" -msgstr "Explosion Ratio" +msgstr "Rapporto di esplosione" msgid "Section View" msgstr "Vista in sezione" @@ -3106,31 +3172,33 @@ msgid "Total Volume:" msgstr "Total Volume:" msgid "Assembly Info" -msgstr "Assembly Info" +msgstr "Informazioni sul montaggio" msgid "Volume:" msgstr "Volume:" msgid "Size:" -msgstr "Size:" +msgstr "Dimensione:" #, c-format, boost-format msgid "" "Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please " "separate the conflicted objects farther (%s <-> %s)." msgstr "" +"Sono stati trovati conflitti di percorsi gcode sul layer %d, z = %.2lf mm. " +"Separare ulteriormente gli oggetti in conflitto (%s <-> %s)." msgid "An object is layed over the boundary of plate." -msgstr "An object is laid over the boundary of the plate." +msgstr "Un oggetto è posizionato oltre il bordo del piatto." msgid "A G-code path goes beyond the max print height." -msgstr "" +msgstr "Un percorso del G-code va oltre l'altezza di stampa massima." msgid "A G-code path goes beyond the boundary of plate." msgstr "A G-code path goes beyond plate boundaries." msgid "Only the object being edit is visible." -msgstr "Only the object being edited is visible." +msgstr "È visibile solo l'oggetto da modificare." msgid "" "An object is laid over the boundary of plate or exceeds the height limit.\n" @@ -3152,37 +3220,37 @@ msgid "Bed leveling" msgstr "Livellamento del piano" msgid "Vibration compensation" -msgstr "" +msgstr "Compensazione delle vibrazioni" msgid "Motor noise cancellation" -msgstr "" +msgstr "Cancellazione del rumore del motore" msgid "Calibration program" -msgstr "Calibration program" +msgstr "Programma calibrazione" msgid "" "The calibration program detects the status of your device automatically to " "minimize deviation.\n" "It keeps the device performing optimally." msgstr "" -"The calibration program detects the status of your device automatically to " -"minimize deviation.\n" -"It keeps the device performing optimally." +"Il programma di calibrazione rileva automaticamente lo stato del dispositivo " +"per ridurre al minimo le deviazioni.\n" +"Questa funzione aiuta il dispositivo a mantenere prestazioni ottimali." msgid "Calibration Flow" -msgstr "Calibration Flow" +msgstr "Calibrazione flusso" msgid "Start Calibration" msgstr "Start Calibration" msgid "No step selected" -msgstr "" +msgstr "Nessun Step selezionato" msgid "Completed" msgstr "Completato" msgid "Calibrating" -msgstr "Calibrating" +msgstr "Calibrazione" msgid "Auto-record Monitoring" msgstr "Monitora registrazione automatica" @@ -3191,7 +3259,7 @@ msgid "Go Live" msgstr "Vai in diretta" msgid "Resolution" -msgstr "Resolution" +msgstr "Risoluzione" msgid "Show \"Live Video\" guide page." msgstr "Mostra pagina della guida \"Diretta Video\"." @@ -3203,17 +3271,17 @@ msgid "1080p" msgstr "1080p" msgid "ConnectPrinter(LAN)" -msgstr "Connect Printer (LAN)" +msgstr "Connetti la stampante (LAN)" msgid "Please input the printer access code:" -msgstr "Please input the printer access code:" +msgstr "Inserisci il codice di accesso alla stampante:" msgid "" "You can find it in \"Settings > Network > Connection code\"\n" "on the printer, as shown in the figure:" msgstr "" -"You can find it in \"Settings > Network > Access code\"\n" -"on the printer, as shown in the figure:" +"Si trova in \"Impostazioni > Rete > Codice di accesso\".\n" +"sulla stampante, come mostrato nella figura:" msgid "Invalid input." msgstr "Ingresso non valido" @@ -3228,25 +3296,25 @@ msgid "Application is closing" msgstr "Closing application" msgid "Closing Application while some presets are modified." -msgstr "Closing Application while some presets are modified." +msgstr "Chiusura dell'applicazione durante la modifica di alcuni preset." msgid "Logging" -msgstr "Logging" +msgstr "Accesso" msgid "Prepare" -msgstr "Prepare" +msgstr "Prepara" msgid "Preview" msgstr "Anteprima" msgid "Device" -msgstr "Device" +msgstr "Dispositivo" msgid "Project" -msgstr "Project" +msgstr "Progetto" msgid "Yes" -msgstr "Yes" +msgstr "Si" msgid "No" msgstr "No" @@ -3255,19 +3323,19 @@ msgid "will be closed before creating a new model. Do you want to continue?" msgstr " verrà chiuso prima di creare un nuovo modello. Vuoi continuare?" msgid "Slice plate" -msgstr "Slice plate" +msgstr "Slice piatto" msgid "Print plate" msgstr "Stampa piatto" msgid "Slice all" -msgstr "Slice all" +msgstr "Slice tutto" msgid "Export G-code file" msgstr "Esporta file G-code" msgid "Send" -msgstr "Send" +msgstr "Invia" msgid "Export plate sliced file" msgstr "Esporta il file del piatto elaborato" @@ -3276,7 +3344,7 @@ msgid "Export all sliced file" msgstr "Esporta tutti i file elaborati" msgid "Print all" -msgstr "Print all" +msgstr "Stampa tutto" msgid "Send all" msgstr "Invia tutto" @@ -3291,13 +3359,13 @@ msgid "Setup Wizard" msgstr "Setup Wizard" msgid "Show Configuration Folder" -msgstr "Show Configuration Folder" +msgstr "Mostra cartella di configurazione" msgid "Show Tip of the Day" msgstr "Suggerimento del giorno" msgid "Check for Update" -msgstr "Check for Updates" +msgstr "Verifica aggiornamenti" msgid "Open Network Test" msgstr "Apri test di rete" @@ -3313,7 +3381,7 @@ msgid "Download Models" msgstr "Scarica Modelli" msgid "Default View" -msgstr "Default View" +msgstr "Vista predefinita" #. TRN To be shown in the main menu View->Top msgid "Top" @@ -3366,22 +3434,22 @@ msgid "Open a project file" msgstr "Apri un file progetto" msgid "Recent projects" -msgstr "Prog&etti recenti" +msgstr "Progetti recenti" msgid "Save Project" -msgstr "Save Project" +msgstr "Salva Progetto" msgid "Save current project to file" -msgstr "Save current project to file" +msgstr "Salva progetto corrente su file" msgid "Save Project as" -msgstr "Save Project as" +msgstr "Salva Progetto come" msgid "Shift+" msgstr "Shift+" msgid "Save current project as" -msgstr "Save current project as" +msgstr "Salva Progetto corrente come" msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF" msgstr "Importa 3MF/STL/STEP/SVG/OBJ/AMF" @@ -3390,22 +3458,22 @@ msgid "Load a model" msgstr "Carica modello" msgid "Import Configs" -msgstr "Import Configs" +msgstr "Importa configurazioni" msgid "Load configs" -msgstr "Load configs" +msgstr "Carica configurazioni" msgid "Import" -msgstr "Import" +msgstr "Importa" msgid "Export all objects as STL" -msgstr "Export All Objects as STL" +msgstr "Esporta tutti gli oggetti come STL" msgid "Export Generic 3MF" -msgstr "Export Generic 3MF" +msgstr "Esporta 3mf generico" msgid "Export 3mf file without using some 3mf-extensions" -msgstr "Export 3mf file without using some 3mf-extensions" +msgstr "Esporta file 3mf senza usare alcune estensioni 3mf" msgid "Export current sliced file" msgstr "Esporta il file elaborato corrente" @@ -3420,7 +3488,7 @@ msgid "Export current plate as G-code" msgstr "Esporta piatto corrente come G-code" msgid "Export &Configs" -msgstr "Export &Configs" +msgstr "Esporta &Configurazioni" msgid "Export current configuration to files" msgstr "Esporta la configurazione corrente in file" @@ -3429,7 +3497,7 @@ msgid "Export" msgstr "Esporta" msgid "Quit" -msgstr "Quit" +msgstr "Esci" msgid "Undo" msgstr "Annulla" @@ -3438,7 +3506,7 @@ msgid "Redo" msgstr "Ripeti" msgid "Cut selection to clipboard" -msgstr "Cut selection to clipboard" +msgstr "Taglia la selezione negli appunti" msgid "Copy" msgstr "Copia" @@ -3465,13 +3533,13 @@ msgid "Deletes all objects" msgstr "Elimina tutti gli oggetti" msgid "Clone selected" -msgstr "Clone Selected" +msgstr "Clone selezionato" msgid "Clone copies of selections" -msgstr "Clone copies of selections" +msgstr "Clonare copie delle selezioni" msgid "Select all" -msgstr "Select All" +msgstr "Seleziona tutto" msgid "Selects all objects" msgstr "Seleziona tutti gli oggetti" @@ -3483,10 +3551,10 @@ msgid "Deselects all objects" msgstr "Deseleziona tutti gli oggetti" msgid "Use Perspective View" -msgstr "Use Perspective View" +msgstr "Usa vista prospettica" msgid "Use Orthogonal View" -msgstr "Use Orthogonal View" +msgstr "Usa vista ortogonale" msgid "Show &Labels" msgstr "Mostra &Etichette" @@ -3510,49 +3578,49 @@ msgid "Help" msgstr "Aiuto" msgid "Temperature Calibration" -msgstr "" +msgstr "Calibrazione della temperatura" msgid "Pass 1" -msgstr "" +msgstr "Passaggio 1" msgid "Flow rate test - Pass 1" -msgstr "" +msgstr "Test di portata - Pass 1" msgid "Pass 2" -msgstr "" +msgstr "Passaggio 2" msgid "Flow rate test - Pass 2" -msgstr "" +msgstr "Test di portata - Pass 2" msgid "Flow rate" -msgstr "" +msgstr "Flusso" msgid "Pressure advance" -msgstr "" +msgstr "Anticipo di pressione" msgid "Retraction test" -msgstr "" +msgstr "Prova di retrazione" msgid "Orca Tolerance Test" -msgstr "" +msgstr "Test di tolleranza dell'orca" msgid "Max flowrate" -msgstr "" +msgstr "Portata massima" msgid "VFA" -msgstr "" +msgstr "VFA" msgid "More..." -msgstr "" +msgstr "Altro..." msgid "Tutorial" -msgstr "" +msgstr "Tutorial" msgid "Calibration help" -msgstr "" +msgstr "Aiuto per la calibrazione" msgid "More calibrations" -msgstr "" +msgstr "Più calibrazioni" msgid "&Open G-code" msgstr "Apri G-code" @@ -3561,7 +3629,7 @@ msgid "Open a G-code file" msgstr "Apri un file G-code" msgid "Re&load from Disk" -msgstr "R&icarica da disco" +msgstr "Ricaricare da disco" msgid "Reload the plater from disk" msgstr "Ricarica piano da disco" @@ -3579,7 +3647,7 @@ msgid "Open Studio" msgstr "Apri Studio" msgid "&Quit" -msgstr "Es&ci" +msgstr "&Esci" #, c-format, boost-format msgid "Quit %s" @@ -3603,13 +3671,13 @@ msgid "A config exists with the same name: %s, do you want to override it." msgstr "Esiste una configurazione con lo stesso nome: %s. Vuoi sovrascriverla?" msgid "Overwrite file" -msgstr "Overwrite file" +msgstr "Sovrascrivi file" msgid "Yes to All" -msgstr "Yes to All" +msgstr "Sì a tutto" msgid "No to All" -msgstr "No to All" +msgstr "No a tutto" msgid "Choose a directory" msgstr "Scegliere una directory" @@ -3618,10 +3686,12 @@ msgstr "Scegliere una directory" msgid "There is %d config exported. (Only non-system configs)" msgid_plural "There are %d configs exported. (Only non-system configs)" msgstr[0] "" +"Viene esportata %d configurazione. (Solo configurazioni non di sistema)" msgstr[1] "" +"Vengono esportate %d configurazioni. (Solo configurazioni non di sistema)" msgid "Export result" -msgstr "Export Result" +msgstr "Risultato Esportazione" msgid "Select profile to load:" msgstr "Seleziona profilo da caricare:" @@ -3631,16 +3701,20 @@ msgid "There is %d config imported. (Only non-system and compatible configs)" msgid_plural "" "There are %d configs imported. (Only non-system and compatible configs)" msgstr[0] "" +"È stata importata %d configurazione. (Solo configurazioni non di sistema e " +"compatibili)" msgstr[1] "" +"Sono state importate %d configurazioni. (Solo configurazioni non di sistema " +"e compatibili)" msgid "Import result" -msgstr "Import result" +msgstr "Risultato dell'importazione" msgid "File is missing" -msgstr "File is missing" +msgstr "File mancante" msgid "The project is no longer available." -msgstr "The project is no longer available." +msgstr "Il progetto non è più disponibile." msgid "Filament Settings" msgstr "Impostazioni Filamento" @@ -3659,10 +3733,10 @@ msgstr "" "3. I presets della stampante" msgid "Synchronization" -msgstr "Synchronization" +msgstr "Sincronizzazione" msgid "Initialize failed (No Device)!" -msgstr "Initialization failed (No Device)!" +msgstr "Inizializzazione fallita (nessun dispositivo)!" msgid "Initialize failed (Device connection not ready)!" msgstr "" @@ -3676,10 +3750,12 @@ msgstr "" "Stampante in fase di caricamento; attendi il completamento del caricamento." msgid "Loading..." -msgstr "Loading..." +msgstr "Caricamento…" msgid "Initialize failed (Not supported on the current printer version)!" msgstr "" +"Inizializzazione non riuscita (non supportata nella versione corrente della " +"stampante)!" msgid "Initialize failed (Not accessible in LAN-only mode)!" msgstr "Inizializzazione fallita (non accessibile in modalità solo LAN)!" @@ -3688,7 +3764,7 @@ msgid "Initialize failed (Missing LAN ip of printer)!" msgstr "Inizializzazione fallita (indirizzo IP LAN della stampante mancante)!" msgid "Initializing..." -msgstr "Initializing..." +msgstr "Inizializzazione ..." #, c-format, boost-format msgid "Initialize failed (%s)!" @@ -3702,7 +3778,7 @@ msgid "Stopped [%d]!" msgstr "Interrotto [%d]!" msgid "Stopped." -msgstr "Stopped." +msgstr "Interrotto." msgid "LAN Connection Failed (Failed to start liveview)" msgstr "Connessione LAN non riuscita (impossibile avviare liveview)" @@ -3734,17 +3810,17 @@ msgid "Information" msgstr "Informazione" msgid "Playing..." -msgstr "Playing..." +msgstr "Riproduzione" #, c-format, boost-format msgid "Load failed [%d]!" -msgstr "Loading failed [%d]!" +msgstr "Caricamento non riuscito [%d]!" msgid "Year" -msgstr "Year" +msgstr "Anno" msgid "Month" -msgstr "Month" +msgstr "Mese" msgid "All Files" msgstr "Tutti i file" @@ -3789,14 +3865,14 @@ msgid "Batch manage files." msgstr "Gestione batch dei file." msgid "No printers." -msgstr "No printers." +msgstr "Nessuna stampante." #, c-format, boost-format msgid "Connect failed [%d]!" -msgstr "Connection failed [%d]!" +msgstr "Connessione non riuscita [%d]!" msgid "Loading file list..." -msgstr "Loading file list..." +msgstr "Caricamento elenco file..." #, c-format, boost-format msgid "No files [%d]" @@ -3811,7 +3887,9 @@ msgid "You are going to delete %u file from printer. Are you sure to continue?" msgid_plural "" "You are going to delete %u files from printer. Are you sure to continue?" msgstr[0] "" +"Stai per eliminare %u file dalla stampante. Sei sicuro di continuare?" msgstr[1] "" +"Stai per eliminare %u i file dalla stampante. Sei sicuro di continuare?" msgid "Delete files" msgstr "Elimina i file" @@ -3836,6 +3914,8 @@ msgid "" "The .gcode.3mf file contains no G-code data.Please slice it with Orca Slicer " "and export a new .gcode.3mf file." msgstr "" +"Il file .gcode.3mf non contiene dati G-code. Taglialo con Orca Slicer ed " +"esporta un nuovo file .gcode.3mf." #, c-format, boost-format msgid "File '%s' was lost! Please download it again." @@ -3858,10 +3938,10 @@ msgid "Downloading %d%%..." msgstr "Scaricamento %d%%..." msgid "Not supported on the current printer version." -msgstr "" +msgstr "Non supportato nella versione corrente della stampante." msgid "Storage unavailable, insert SD card." -msgstr "" +msgstr "Memoria non disponibile, inserire la scheda SD." msgid "Speed:" msgstr "Velocità:" @@ -3885,28 +3965,28 @@ msgid "Swap Y/Z axes" msgstr "Inverti assi Y/Z" msgid "Invert X axis" -msgstr "" +msgstr "Inverti asse X" msgid "Invert Y axis" -msgstr "" +msgstr "Inverti asse Y" msgid "Invert Z axis" -msgstr "" +msgstr "Inverti asse Z" msgid "Invert Yaw axis" -msgstr "" +msgstr "Inverti asse di imbardata" msgid "Invert Pitch axis" -msgstr "" +msgstr "Inverti asse passo" msgid "Invert Roll axis" -msgstr "" +msgstr "Inverti asse di rollio" msgid "Printing Progress" -msgstr "Printing progress" +msgstr "Progresso di stampa" msgid "Resume" -msgstr "Resume" +msgstr "Continua" msgid "Stop" msgstr "Stop" @@ -3924,17 +4004,22 @@ msgid "" "You have completed printing the mall model, \n" "but the synchronization of rating information has failed." msgstr "" +"Hai completato la stampa del modello del centro commerciale, \n" +"Tuttavia, la sincronizzazione delle informazioni di valutazione non è " +"riuscita." msgid "How do you like this printing file?" -msgstr "" +msgstr "Ti piace questo file di stampa?" msgid "" "(The model has already been rated. Your rating will overwrite the previous " "rating.)" msgstr "" +"(Il modello è già stato valutato. La tua valutazione sovrascriverà la " +"valutazione precedente.)" msgid "Rate" -msgstr "" +msgstr "Tasso" msgid "Camera" msgstr "Camera" @@ -3946,7 +4031,7 @@ msgid "Camera Setting" msgstr "Impostazioni camera" msgid "Control" -msgstr "Control" +msgstr "Controllo" msgid "Print Options" msgstr "Opzioni stampa" @@ -3967,10 +4052,10 @@ msgid "Bed" msgstr "Piano" msgid "Unload" -msgstr "Unload" +msgstr "Scarica" msgid "Debug Info" -msgstr "Debug Info" +msgstr "Informazioni di debug" msgid "No SD Card" msgstr "Nessuna scheda microSD" @@ -4042,101 +4127,116 @@ msgid "Can't start this without SD card." msgstr "Impossibile iniziare senza scheda MicroSD." msgid "Rate the Print Profile" -msgstr "" +msgstr "Valutare il profilo di stampa" msgid "Comment" -msgstr "" +msgstr "Commento" msgid "Rate this print" -msgstr "" +msgstr "Vota questa stampa" msgid "Add Photo" -msgstr "" +msgstr "Aggiungi foto" msgid "Delete Photo" -msgstr "" +msgstr "Elimina foto" msgid "Submit" -msgstr "" +msgstr "Invia" msgid "Please click on the star first." -msgstr "" +msgstr "Fare clic prima sulla stella." msgid "InFo" -msgstr "" +msgstr "info" msgid "Get oss config failed." -msgstr "" +msgstr "Ottenere la configurazione di oss non riuscita." msgid "Upload Pictrues" -msgstr "" +msgstr "Carica foto" msgid "Number of images successfully uploaded" -msgstr "" +msgstr "Numero di immagini caricate con successo" msgid " upload failed" -msgstr "" +msgstr " Caricamento non riuscito" msgid " upload config prase failed\n" -msgstr "" +msgstr " Caricamento della configurazione prase non riuscita\n" msgid " No corresponding storage bucket\n" -msgstr "" +msgstr " Nessun secchio di stoccaggio corrispondente\n" msgid " can not be opened\n" -msgstr "" +msgstr " non può essere aperto\n" msgid "" "The following issues occurred during the process of uploading images. Do you " "want to ignore them?\n" "\n" msgstr "" +"Durante il processo di caricamento delle immagini si sono verificati i " +"seguenti problemi. Vuoi ignorarli?\n" +"\n" msgid "info" msgstr "info" msgid "Synchronizing the printing results. Please retry a few seconds later." msgstr "" +"Sincronizzazione dei risultati di stampa. Riprova qualche secondo dopo." msgid "Upload failed\n" -msgstr "" +msgstr "Caricamento non riuscito\n" msgid "obtaining instance_id failed\n" -msgstr "" +msgstr "ottenere instance_id non è riuscito\n" msgid "" "Your comment result cannot be uploaded due to some reasons. As follows:\n" "\n" " error code: " msgstr "" +"Il risultato del tuo commento non può essere caricato per alcuni motivi. " +"Come segue:\n" +"\n" +"Codice di errore: " msgid "error message: " -msgstr "" +msgstr "Mostra messaggio d'errore" msgid "" "\n" "\n" "Would you like to redirect to the webpage for rating?" msgstr "" +"\n" +"Vuoi reindirizzare alla pagina web per la valutazione?" msgid "" "Some of your images failed to upload. Would you like to redirect to the " "webpage for rating?" msgstr "" +"Alcune delle tue immagini non sono state caricate. Vuoi reindirizzare alla " +"pagina web per la valutazione?" msgid "You can select up to 16 images." -msgstr "" +msgstr "È possibile selezionare fino a 16 immagini." msgid "" "At least one successful print record of this print profile is required \n" "to give a positive rating(4 or 5stars)." msgstr "" +"È necessario almeno un record di stampa riuscito di questo profilo di " +"stampa \n" +"per dare una valutazione positiva (4 o 5 stelle)." msgid "Status" msgstr "Stato" msgid "Update" -msgstr "Update" +msgstr "Aggiorna" msgid "HMS" msgstr "HMS" @@ -4169,25 +4269,25 @@ msgid "%s information" msgstr "%s informazioni" msgid "Skip" -msgstr "Skip" +msgstr "Salta" msgid "3D Mouse disconnected." msgstr "Mouse 3D disconnesso." msgid "Configuration can update now." -msgstr "A new configuration is available. Update now?" +msgstr "È disponibile una nuova configurazione. Aggiorna adesso?" msgid "Detail." -msgstr "More" +msgstr "Altro" msgid "Integration was successful." -msgstr "Integration was successful." +msgstr "L'integrazione è avvenuta con successo." msgid "Integration failed." -msgstr "Integration failed." +msgstr "Integrazione fallita." msgid "Undo integration was successful." -msgstr "Undo integration was successful." +msgstr "Annullamento integrazione riuscita." msgid "New network plug-in available." msgstr "Nuovo plug-in di network disponibile" @@ -4196,16 +4296,16 @@ msgid "Details" msgstr "Dettagli" msgid "Undo integration failed." -msgstr "Undo integration failed." +msgstr "Annullamento integrazione non riuscito." msgid "Exporting." -msgstr "Esportazione." +msgstr "Esportazione" msgid "Software has New version." -msgstr "An update is available!" +msgstr "Aggiornamento disponibile!" msgid "Goto download page." -msgstr "Go to download page" +msgstr "Vai alla pagina di download" msgid "Open Folder." msgstr "Apri Cartella." @@ -4216,20 +4316,20 @@ msgstr "Rimuovi l'hardware in modo sicuro." #, c-format, boost-format msgid "%1$d Object has custom supports." msgid_plural "%1$d Objects have custom supports." -msgstr[0] "%1$d Object has custom supports." +msgstr[0] "%1$d L'oggetto ha supporti personalizzati." msgstr[1] "%1$d Objects have custom supports." #, c-format, boost-format msgid "%1$d Object has color painting." msgid_plural "%1$d Objects have color painting." -msgstr[0] "%1$d Object has color painting." +msgstr[0] "%1$d L'oggetto ha una pittura a colori." msgstr[1] "%1$d Objects have color painting." #, c-format, boost-format msgid "%1$d object was loaded as a part of cut object." msgid_plural "%1$d objects were loaded as parts of cut object" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%1$d oggetto è stato caricato come parte di un oggetto tagliato." +msgstr[1] "%1$d oggetti sono stati caricati come parti di un oggetto tagliato" msgid "ERROR" msgstr "ERRORE" @@ -4244,25 +4344,25 @@ msgid "Cancel upload" msgstr "Annulla caricamento" msgid "Slice ok." -msgstr "Slice complete" +msgstr "Slice completo" msgid "Jump to" -msgstr "Jump to" +msgstr "Vai a" msgid "Error:" -msgstr "Error:" +msgstr "Errore:" msgid "Warning:" -msgstr "Warning:" +msgstr "Attenzione:" msgid "Export successfully." msgstr "Esportato correttamente" msgid "Model file downloaded." -msgstr "" +msgstr "File del modello scaricato." msgid "Serious warning:" -msgstr "" +msgstr "Avvertimento serio:" msgid " (Repair)" msgstr " (Repair)" @@ -4274,16 +4374,16 @@ msgid "WARNING:" msgstr "ATTENZIONE:" msgid "Your model needs support ! Please make support material enable." -msgstr "Your model needs support! Please enable support material." +msgstr "Il modello necessita di supporti! Abilita i materiali di supporto." msgid "Gcode path overlap" -msgstr "G-code path overlap" +msgstr "Sovrapposizione del percorso G-code" msgid "Support painting" -msgstr "Support Painting" +msgstr "Pittura Supporti" msgid "Color painting" -msgstr "Color Painting" +msgstr "Pittura a colori" msgid "Cut connectors" msgstr "Taglia connettori" @@ -4298,11 +4398,11 @@ msgid "" "The application cannot run normally because OpenGL version is lower than " "2.0.\n" msgstr "" -"The application cannot run normally because your OpenGL version is lower " -"than 2.0.\n" +"L'applicazione non può essere eseguita normalmente perché la versione OpenGL " +"è precedente alla 2.0.\n" msgid "Please upgrade your graphics card driver." -msgstr "Please upgrade your graphics card driver." +msgstr "Aggiorna i driver della scheda grafica." msgid "Unsupported OpenGL version" msgstr "Versione OpenGL non supportata" @@ -4312,7 +4412,7 @@ msgid "" "Unable to load shaders:\n" "%s" msgstr "" -"Unable to load shaders:\n" +"Impossibile caricare gli shader:\n" "%s" msgid "Error loading shaders" @@ -4349,56 +4449,56 @@ msgid "Auto-recovery from step loss" msgstr "Recupero automatico perdita passi" msgid "Allow Prompt Sound" -msgstr "" +msgstr "Consenti suono di richiesta" msgid "Global" -msgstr "Global" +msgstr "Globale" msgid "Objects" -msgstr "Objects" +msgstr "Oggetti" msgid "Advance" -msgstr "Advanced" +msgstr "Avanzato " msgid "Compare presets" msgstr "Confronta i preset" msgid "View all object's settings" -msgstr "View all object's settings" +msgstr "Visualizza tutte le impostazioni oggetto" msgid "Filament settings" -msgstr "Filament settings" +msgstr "Impostazioni Filamento" msgid "Printer settings" -msgstr "Printer settings" +msgstr "Impostazioni stampante" msgid "Remove current plate (if not last one)" -msgstr "" +msgstr "Rimuovere la piastra corrente (se non l'ultima)" msgid "Auto orient objects on current plate" -msgstr "" +msgstr "Orienta automaticamente gli oggetti sulla piastra corrente" msgid "Arrange objects on current plate" -msgstr "" +msgstr "Disporre gli oggetti sulla piastra corrente" msgid "Unlock current plate" -msgstr "" +msgstr "Sblocca la piastra corrente" msgid "Lock current plate" -msgstr "" +msgstr "Piastra corrente di blocco" msgid "Customize current plate" -msgstr "" +msgstr "Personalizza la piastra corrente" msgid "Untitled" msgstr "Senza titolo" #, boost-format msgid " plate %1%:" -msgstr " plate %1%:" +msgstr " Piatto %1%:" msgid "Invalid name, the following characters are not allowed:" -msgstr "Invalid name, the following characters are not allowed:" +msgstr "Nome non valido, i seguenti caratteri non sono consentiti:" msgid "Sliced Info" msgstr "Informazioni processo" @@ -4413,13 +4513,13 @@ msgid "Used Filament (g)" msgstr "Filamento usato (g)" msgid "Used Materials" -msgstr "Used Materials" +msgstr "Materiali usati" msgid "Estimated time" msgstr "Estimated time" msgid "Filament changes" -msgstr "Filament changes" +msgstr "Cambi filamento" msgid "Click to edit preset" msgstr "Clicca per modificare il preset" @@ -4428,10 +4528,10 @@ msgid "Connection" msgstr "Connessione" msgid "Bed type" -msgstr "Bed type" +msgstr "Tipo di piatto" msgid "Flushing volumes" -msgstr "Flushing volumes" +msgstr "Volumi di spurgo" msgid "Add one filament" msgstr "Aggiungere un filamento" @@ -4483,10 +4583,13 @@ msgid "" "Orca Slicer or restart Orca Slicer to check if there is an update to system " "presets." msgstr "" +"Ci sono alcuni filamenti sconosciuti mappati su un preset generico. Aggiorna " +"Orca Slicer o riavvia Orca Slicer per verificare se è presente un " +"aggiornamento delle impostazioni di sistema." #, boost-format msgid "Do you want to save changes to \"%1%\"?" -msgstr "Do you want to save changes to \"%1%\"?" +msgstr "Vuoi salvare le modifiche a \"%1%\"?" #, c-format, boost-format msgid "" @@ -4502,16 +4605,20 @@ msgstr "Espulsione del dispositivo %s (%s) non riuscita." msgid "Previous unsaved project detected, do you want to restore it?" msgstr "" -"Previously unsaved items have been detected. Do you want to restore them?" +"Sono stati rilevati elementi precedentemente non salvati. Vuoi ripristinarli?" msgid "Restore" -msgstr "Restore" +msgstr "Ripristina" msgid "" "The current hot bed temperature is relatively high. The nozzle may be " "clogged when printing this filament in a closed enclosure. Please open the " "front door and/or remove the upper glass." msgstr "" +"L'attuale temperatura del letto caldo è relativamente alta. L'ugello " +"potrebbe essere ostruito quando si stampa questo filamento in un involucro " +"chiuso. Si prega di aprire lo sportello anteriore e/o rimuovere il vetro " +"superiore." msgid "" "The nozzle hardness required by the filament is higher than the default " @@ -4526,10 +4633,12 @@ msgid "" "Enabling traditional timelapse photography may cause surface imperfections. " "It is recommended to change to smooth mode." msgstr "" +"L'attivazione della fotografia timelapse tradizionale può causare " +"imperfezioni della superficie. Si consiglia di passare alla modalità liscia." #, c-format, boost-format msgid "Loading file: %s" -msgstr "Loading file: %s" +msgstr "Caricamento file: %s" msgid "The 3mf is not supported by OrcaSlicer, load geometry data only." msgstr "Il 3mf non proviene da Orca Slicer, carica solo dati geometrici." @@ -4542,6 +4651,8 @@ msgstr "La configurazione non può essere caricata." msgid "The 3mf is generated by old Orca Slicer, load geometry data only." msgstr "" +"Il 3mf è stato generato da una vecchia versione di Orca Slicer, caricando \n" +"solo i dati geometrici." #, c-format, boost-format msgid "" @@ -4552,10 +4663,10 @@ msgstr "" "chiavi non riconosciute:" msgid "You'd better upgrade your software.\n" -msgstr "You should update your software.\n" +msgstr "Devi aggiornare il software.\n" msgid "Newer 3mf version" -msgstr "Newer 3mf version" +msgstr "Versione 3mf più recente" #, c-format, boost-format msgid "" @@ -4578,31 +4689,35 @@ msgid "Incompatible 3mf" msgstr "Incompatible 3mf" msgid "Name of components inside step file is not UTF8 format!" -msgstr "Component name(s) inside step file not in UTF8 format!" +msgstr "" +"Il nome/i del componente all'interno del file step non è in formato UTF8!" msgid "The name may show garbage characters!" -msgstr "Because of unsupported text encoding, garbage characters may appear!" +msgstr "" +"A causa di una codifica del testo non supportata, potrebbero apparire " +"caratteri inutili!" #, boost-format msgid "Failed loading file \"%1%\". An invalid configuration was found." -msgstr "Failed loading file \"%1%\". An invalid configuration was found." +msgstr "" +"Caricamento del file \"%1%\" non riuscito. Trovata configurazione non valida." msgid "Objects with zero volume removed" -msgstr "Objects with zero volume removed" +msgstr "Oggetti con volume zero rimossi" msgid "The volume of the object is zero" -msgstr "The volume of the object is zero" +msgstr "Il volume dell'oggetto è zero" #, c-format, boost-format msgid "" "The object from file %s is too small, and maybe in meters or inches.\n" " Do you want to scale to millimeters?" msgstr "" -"The object from file %s is too small, and may be in meters or inches.\n" -" Do you want to scale to millimeters?" +"L'oggetto del file %s è troppo piccolo e può essere in metri o pollici.\n" +" Si desidera scalare in millimetri?" msgid "Object too small" -msgstr "Object too small" +msgstr "Oggetto troppo piccolo" msgid "" "This file contains several objects positioned at multiple heights.\n" @@ -4617,13 +4732,13 @@ msgid "Multi-part object detected" msgstr "Rilevato oggetto in più parti" msgid "Load these files as a single object with multiple parts?\n" -msgstr "Load these files as a single object with multiple parts?\n" +msgstr "Caricare questi file come un singolo oggetto con più parti?\n" msgid "Object with multiple parts was detected" -msgstr "An object with multiple parts was detected" +msgstr "È stato rilevato un oggetto con più parti" msgid "The file does not contain any geometry data." -msgstr "The file does not contain any geometry data." +msgstr "Il file non contiene dati geometrici." msgid "" "Your object appears to be too large, Do you want to scale it down to fit the " @@ -4639,13 +4754,13 @@ msgid "Export STL file:" msgstr "Esporta file STL:" msgid "Export AMF file:" -msgstr "" +msgstr "Esporta file AMF:" msgid "Save file as:" msgstr "Salva come:" msgid "Export OBJ file:" -msgstr "" +msgstr "Esporta file OBJ:" msgid "Delete object which is a part of cut object" msgstr "Elimina l'oggetto che fa parte dell'oggetto tagliato" @@ -4660,16 +4775,16 @@ msgstr "" "In seguito, la coerenza del modello non può essere garantita." msgid "The selected object couldn't be split." -msgstr "The selected object couldn't be split." +msgstr "L'oggetto selezionato non può essere diviso." msgid "Another export job is running." -msgstr "Another export job is running." +msgstr "È in esecuzione un altro processo di esportazione." msgid "Replace from:" -msgstr "" +msgstr "Sostituisci da:" msgid "Unable to replace with more than one volume" -msgstr "" +msgstr "Impossibile sostituire con più di un volume" msgid "Error during replace" msgstr "Errore durante la sostituzione" @@ -4681,31 +4796,31 @@ msgid "File for the replace wasn't selected" msgstr "Il file per la sostituzione non è stato selezionato" msgid "Please select a file" -msgstr "Please select a file" +msgstr "Seleziona file" msgid "Do you want to replace it" -msgstr "" +msgstr "Vuoi sostituirlo" msgid "Message" -msgstr "" +msgstr "Messaggio" msgid "Reload from:" -msgstr "" +msgstr "Ricarica da:" msgid "Unable to reload:" -msgstr "" +msgstr "Impossibile ricaricare:" msgid "Error during reload" -msgstr "" +msgstr "Errore durante il ri-caricamento" msgid "Slicing" msgstr "Slicing" msgid "There are warnings after slicing models:" -msgstr "There are warnings after slicing models:" +msgstr "Ci sono avvisi dopo aver elaborato i modelli:" msgid "warnings" -msgstr "warnings" +msgstr "Avvisi" msgid "Invalid data" msgstr "Dati non validi" @@ -4715,10 +4830,10 @@ msgstr "Slicing Annullato" #, c-format, boost-format msgid "Slicing Plate %d" -msgstr "Slicing Plate %d" +msgstr "Slicing Piatto %d" msgid "Please resolve the slicing errors and publish again." -msgstr "Please resolve the slicing errors and publish again." +msgstr "Risolvi gli errori di slicing e pubblica nuovamente." msgid "" "Network Plug-in is not detected. Network related features are unavailable." @@ -4734,13 +4849,14 @@ msgstr "" "Il file caricato contiene solo G-code, non può accedere alla pagina Prepara" msgid "You can keep the modified presets to the new project or discard them" -msgstr "You can keep the modified presets for the new project or discard them" +msgstr "" +"È possibile conservare i preset modificati per il nuovo progetto o eliminarli" msgid "Creating a new project" msgstr "Creazione nuovo progetto" msgid "Load project" -msgstr "Load project" +msgstr "Carica progetto" msgid "" "Failed to save the project.\n" @@ -4771,6 +4887,8 @@ msgid "" "Importing to Orca Slicer failed. Please download the file and manually " "import it." msgstr "" +"L'importazione di Orca Slicer non è riuscita. Si prega di scaricare il file " +"e importarlo manualmente." msgid "The selected file" msgstr "Il file selezionato" @@ -4779,13 +4897,13 @@ msgid "does not contain valid gcode." msgstr "non contiene un g-code valido." msgid "Error occurs while loading G-code file" -msgstr "An Error has occurred while loading the G-code file." +msgstr "Si è verificato un errore durante il caricamento del file G-code." msgid "Drop project file" -msgstr "Drop project file" +msgstr "Elimina il file di progetto" msgid "Please select an action" -msgstr "Please select an action" +msgstr "Seleziona un'azione" msgid "Open as project" msgstr "Apri come progetto" @@ -4794,33 +4912,33 @@ msgid "Import geometry only" msgstr "Importa solo la geometria" msgid "Only one G-code file can be opened at the same time." -msgstr "Only one G-code file can be opened at a time." +msgstr "È possibile aprire un solo file G-code alla volta." msgid "G-code loading" -msgstr "G-code loading" +msgstr "Caricamento G-code" msgid "G-code files can not be loaded with models together!" -msgstr "G-code files and models cannot be loaded together!" +msgstr "I file e i modelli G-code non possono essere caricati insieme!" msgid "Can not add models when in preview mode!" -msgstr "Unable to add models in preview mode" +msgstr "Impossibile aggiungere modelli in modalità anteprima" msgid "Add Models" -msgstr "Add Models" +msgstr "Aggiungi modelli" msgid "All objects will be removed, continue?" msgstr "Saranno rimossi tutti gli oggetti, continuare?" msgid "The current project has unsaved changes, save it before continue?" msgstr "" -"The current project has unsaved changes. Would you like to save before " -"continuing?" +"Il progetto corrente ha modifiche non salvate. Desideri salvarle prima di " +"continuare?" msgid "Remember my choice." msgstr "Ricorda la mia scelta" msgid "Number of copies:" -msgstr "Number of copies:" +msgstr "Numero di copie:" msgid "Copies of the selected object" msgstr "Copie dell'oggetto selezionato" @@ -4829,16 +4947,16 @@ msgid "Save G-code file as:" msgstr "Salva il file G-code come:" msgid "Save SLA file as:" -msgstr "" +msgstr "Salva il file SLA come:" msgid "The provided file name is not valid." -msgstr "" +msgstr "Il nome del file fornito non è valido." msgid "The following characters are not allowed by a FAT file system:" -msgstr "" +msgstr "I seguenti caratteri non sono permessi da un file system FAT:" msgid "Save Sliced file as:" -msgstr "Save Sliced file as:" +msgstr "Salva file elaborato come:" #, c-format, boost-format msgid "" @@ -4857,16 +4975,18 @@ msgstr "" msgid "Is the printer ready? Is the print sheet in place, empty and clean?" msgstr "" +"La stampante è pronta? La piastra di stampa è in posizione, vuota e pulita?" msgid "Upload and Print" -msgstr "" +msgstr "Carica e Stampa" msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." msgstr "" -"Print By Object: \n" -"We suggest using auto-arrange to avoid collisions when printing." +"Stampa per oggetto: \n" +"Ti consigliamo di utilizzare la disposizione automatica per evitare " +"collisioni durante la stampa." msgid "Send G-code" msgstr "Invia G-code" @@ -4875,10 +4995,12 @@ msgid "Send to printer" msgstr "Manda alla stampante" msgid "Custom supports and color painting were removed before repairing." -msgstr "Custom supports and color painting were removed before repairing." +msgstr "" +"I supporti personalizzati e la pittura a colori sono stati rimossi prima " +"della riparazione." msgid "Invalid number" -msgstr "Invalid number" +msgstr "Numero non valido" msgid "Plate Settings" msgstr "Impostazioni Piatto" @@ -4889,23 +5011,23 @@ msgstr "Numero attuale di parti selezionate: %1%\n" #, boost-format msgid "Number of currently selected objects: %1%\n" -msgstr "" +msgstr "Numero di oggetti attualmente selezionati: %1%\n" #, boost-format msgid "Part name: %1%\n" -msgstr "Part name: %1%\n" +msgstr "Nome parte: %1%\n" #, boost-format msgid "Object name: %1%\n" -msgstr "Object name: %1%\n" +msgstr "Nome oggetto: %1%\n" #, boost-format msgid "Size: %1% x %2% x %3% in\n" -msgstr "Size: %1% x %2% x %3% in\n" +msgstr "Dimensioni: %1% x %2% x %3% in\n" #, boost-format msgid "Size: %1% x %2% x %3% mm\n" -msgstr "Size: %1% x %2% x %3% mm\n" +msgstr "Dimensioni: %1% x %2% x %3% mm\n" #, boost-format msgid "Volume: %1% in³\n" @@ -4917,7 +5039,7 @@ msgstr "Volume: %1% mm³\n" #, boost-format msgid "Triangles: %1%\n" -msgstr "Triangles: %1%\n" +msgstr "Triangoli: %1%\n" msgid "Tips:" msgstr "Suggerimenti:" @@ -4926,6 +5048,8 @@ msgid "" "\"Fix Model\" feature is currently only on Windows. Please repair the model " "on Orca Slicer(windows) or CAD softwares." msgstr "" +"La funzione \"Correggi modello\" è attualmente disponibile solo su Windows. " +"Si prega di riparare il modello su Orca Slicer (Windows) o software CAD." #, c-format, boost-format msgid "" @@ -4938,28 +5062,28 @@ msgstr "" "un numero diverso da zero." msgid "Switching the language requires application restart.\n" -msgstr "Switching languages requires the application to restart.\n" +msgstr "Il cambio lingua richiede il riavvio dell'applicazione.\n" msgid "Do you want to continue?" -msgstr "Do you want to continue?" +msgstr "Vuoi continuare?" msgid "Language selection" msgstr "Selezione lingua" msgid "Switching application language while some presets are modified." -msgstr "Switching application language while some presets are modified." +msgstr "Cambio lingua applicazione durante la modifica di alcuni preset." msgid "Changing application language" -msgstr "Changing application language" +msgstr "Modifica lingua applicazione" msgid "Changing the region will log out your account.\n" -msgstr "Changing the region will log you out of your account.\n" +msgstr "La modifica della regione ti disconnetterà dal tuo account.\n" msgid "Region selection" -msgstr "Region selection" +msgstr "Selezione Paese" msgid "Second" -msgstr "Second" +msgstr "Secondo" msgid "Browse" msgstr "Sfoglia" @@ -4968,7 +5092,7 @@ msgid "Choose Download Directory" msgstr "Scegliere la directory di download" msgid "General Settings" -msgstr "General Settings" +msgstr "Impostazioni generali" msgid "Asia-Pacific" msgstr "Asia-Pacific" @@ -4983,31 +5107,31 @@ msgid "North America" msgstr "North America" msgid "Others" -msgstr "Others" +msgstr "Altro" msgid "Login Region" -msgstr "Login Region" +msgstr "Regione di accesso" msgid "Stealth Mode" -msgstr "" +msgstr "Modalità invisibile" msgid "Metric" -msgstr "Metric" +msgstr "Metrico" msgid "Imperial" -msgstr "Imperial" +msgstr "Imperiale" msgid "Units" -msgstr "Units" +msgstr "Unità" msgid "Home" -msgstr "" +msgstr "Home" msgid "Default Page" -msgstr "" +msgstr "Pagina predefinita" msgid "Set the page opened on startup." -msgstr "" +msgstr "Imposta la pagina aperta all'avvio." msgid "Zoom to mouse position" msgstr "Zoom posizione del mouse" @@ -5020,16 +5144,17 @@ msgstr "" "anziché verso il centro della finestra 2D." msgid "Use free camera" -msgstr "" +msgstr "Usa l'inquadratura libera" msgid "If enabled, use free camera. If not enabled, use constrained camera." msgstr "" +"Se attivo, usa la visuale libera. Se non attivo, usa la visuale vincolata." msgid "Show splash screen" -msgstr "" +msgstr "Mostra splash screen" msgid "Show the splash screen during startup." -msgstr "" +msgstr "Mostra la schermata iniziale durante l'avvio." msgid "Show \"Tip of the day\" notification after start" msgstr "Mostra \"Suggerimento del giorno\" dopo l'avvio" @@ -5038,16 +5163,17 @@ msgid "If enabled, useful hints are displayed at startup." msgstr "Se abilitato, all'avvio vengono visualizzati suggerimenti utili." msgid "Show g-code window" -msgstr "" +msgstr "Mostra la finestra del codice g" msgid "If enabled, g-code window will be displayed." -msgstr "" +msgstr "Se abilitato, verrà visualizzata la finestra del codice g." msgid "Presets" msgstr "Preset" msgid "Auto sync user presets(Printer/Filament/Process)" -msgstr "Auto sync user presets (Printer/Filament/Process)" +msgstr "" +"Sincronizzazione automatica preset utente (stampante/filamento/processo)" msgid "User Sync" msgstr "User Sync" @@ -5065,28 +5191,32 @@ msgid "Associate files to OrcaSlicer" msgstr "Associate files to Orca Slicer" msgid "Associate .3mf files to OrcaSlicer" -msgstr "Associate .3mf files to Orca Slicer" +msgstr "Associare file .3mf a OrcaSlicer" msgid "If enabled, sets OrcaSlicer as default application to open .3mf files" msgstr "" -"If enabled, this sets Orca Slicer as the default application to open .3mf " -"files." +"Se abilitata, imposta OrcaSlicer come applicazione predefinita per aprire " +"\"\n" +"\"i file .3mf." msgid "Associate .stl files to OrcaSlicer" msgstr "Associate .stl files to Orca Slicer" msgid "If enabled, sets OrcaSlicer as default application to open .stl files" msgstr "" -"If enabled, this sets Orca Slicer as the default application to open .stl " -"files." +"Se abilitata, imposta OrcaSlicer come applicazione predefinita per aprire " +"\"\n" +"\"i file .stl." msgid "Associate .step/.stp files to OrcaSlicer" msgstr "Associate .step/.stp files to Orca Slicer" msgid "If enabled, sets OrcaSlicer as default application to open .step files" msgstr "" -"If enabled, this sets Orca Slicer as the default application to open .step " -"files." +"Se abilitata, imposta OrcaSlicer come applicazione predefinita per aprire " +"\"\n" +"\"i file .step." + msgid "Maximum recent projects" msgstr "Numero massimo di progetti recenti" @@ -5098,7 +5228,7 @@ msgid "Clear my choice on the unsaved projects." msgstr "Cancella la mia scelta sui progetti non salvati." msgid "Auto-Backup" -msgstr "Auto-Backup" +msgstr "Backup automatico" msgid "" "Backup your project periodically for restoring from the occasional crash." @@ -5122,40 +5252,40 @@ msgid "Enable Dark mode" msgstr "Attiva modalità Scura" msgid "Develop mode" -msgstr "Developer mode" +msgstr "Modalità sviluppatore" msgid "Skip AMS blacklist check" msgstr "Salta check Blacklist AMS" msgid "Home page and daily tips" -msgstr "Home page and daily tips" +msgstr "Home page e suggerimenti quotidiani" msgid "Show home page on startup" -msgstr "Show home page on startup" +msgstr "Mostra pagina iniziale all'avvio" msgid "Sync settings" -msgstr "Sync settings" +msgstr "Impostazioni sincronizzazione" msgid "User sync" msgstr "User sync" msgid "Preset sync" -msgstr "Preset sync" +msgstr "Sincronizzazione preset" msgid "Preferences sync" -msgstr "Preferences sync" +msgstr "Sincronizzazione preferenze" msgid "View control settings" -msgstr "View control settings" +msgstr "Visualizza impostazioni di controllo" msgid "Rotate of view" -msgstr "Rotate View" +msgstr "Ruota vista" msgid "Move of view" -msgstr "Pan View" +msgstr "Vista panoramica" msgid "Zoom of view" -msgstr "Zoom View" +msgstr "Vista Zoom" msgid "Other" msgstr "Altro" @@ -5173,25 +5303,25 @@ msgid "Internal developer mode" msgstr "Modalità sviluppatore interno" msgid "Log Level" -msgstr "Log Level" +msgstr "Livello log" msgid "fatal" -msgstr "fatal" +msgstr "fatale" msgid "error" -msgstr "error" +msgstr "Errore" msgid "warning" -msgstr "warning" +msgstr "attenzione" msgid "debug" msgstr "debug" msgid "trace" -msgstr "trace" +msgstr "Traccia" msgid "Host Setting" -msgstr "Host Setting" +msgstr "Impostazione host" msgid "DEV host: api-dev.bambu-lab.com/v1" msgstr "DEV host: api-dev.bambu-lab.com/v1" @@ -5203,16 +5333,16 @@ msgid "PRE host: api-pre.bambu-lab.com/v1" msgstr "PRE host: api-pre.bambu-lab.com/v1" msgid "Product host" -msgstr "Product host" +msgstr "Host del prodotto" msgid "debug save button" -msgstr "Debug save button" +msgstr "Pulsante salvataggio debug" msgid "save debug settings" -msgstr "save debug settings" +msgstr "salva impostazioni debug" msgid "DEBUG settings have saved successfully!" -msgstr "Debug settings have been saved successfully!" +msgstr "Le impostazioni di debug sono state salvate correttamente!" msgid "Switch cloud environment, Please login again!" msgstr "Cambia ambiente cloud; Effettua nuovamente il login!" @@ -5227,10 +5357,10 @@ msgid "Incompatible presets" msgstr "Preset incompatibili" msgid "AMS filaments" -msgstr "AMS filament" +msgstr "Filamento AMS" msgid "Click to pick filament color" -msgstr "Click to select filament color" +msgstr "Fai click per selezionare il colore del filamento" msgid "Please choose the filament colour" msgstr "Si prega di scegliere il colore del filamento" @@ -5242,7 +5372,7 @@ msgid "Edit preset" msgstr "Modifica preset" msgid "Project-inside presets" -msgstr "Project-inside presets" +msgstr "Preset interno al progetto" msgid "Add/Remove filaments" msgstr "Aggiungi/rimuovi filamento" @@ -5257,7 +5387,7 @@ msgid "Incompatible" msgstr "Non compatibile" msgid "The selected preset is null!" -msgstr "" +msgstr "Il preset selezionato è nullo!" msgid "Plate name" msgstr "Nome Piatto" @@ -5266,13 +5396,13 @@ msgid "Same as Global Print Sequence" msgstr "Uguale a Sequenza stampa globale" msgid "Print sequence" -msgstr "Print sequence" +msgstr "Sequenza di stampa" msgid "Customize" -msgstr "" +msgstr "Personalizza" msgid "First layer filament sequence" -msgstr "" +msgstr "Sequenza di filamenti di primo Layer" msgid "Same as Global Plate Type" msgstr "Uguale al tipo di piano globale" @@ -5293,80 +5423,82 @@ msgid "Log Out" msgstr "Log Out" msgid "Slice all plate to obtain time and filament estimation" -msgstr "Slice all plates to obtain time and filament estimation" +msgstr "Slice di tutti i piatti per ottenere una stima del tempo e filamento" msgid "Packing project data into 3mf file" -msgstr "Packing project data into 3mf file" +msgstr "Packing dati progetto in 3mf file" msgid "Uploading 3mf" -msgstr "Uploading 3mf" +msgstr "Caricamento 3mf" msgid "Jump to model publish web page" -msgstr "Jump to model publish web page" +msgstr "Vai alla pagina web di pubblicazione del modello" msgid "Note: The preparation may takes several minutes. Please be patiant." -msgstr "Note: The preparation may take several minutes. Please be patient." +msgstr "" +"Nota: la preparazione può richiedere alcuni minuti. Si prega di avere " +"pazienza." msgid "Publish" -msgstr "Publish" +msgstr "Pubblica" msgid "Publish was cancelled" -msgstr "Publish was cancelled" +msgstr "La pubblicazione è stata annullata" msgid "Slicing Plate 1" -msgstr "Slicing Plate 1" +msgstr "Slicing Piatto 1" msgid "Packing data to 3mf" msgstr "Packing data to 3mf" msgid "Jump to webpage" -msgstr "Jump to webpage" +msgstr "Vai alla pagina web" #, c-format, boost-format msgid "Save %s as" -msgstr "Save %s as" +msgstr "Salva %s come" msgid "User Preset" -msgstr "User Preset" +msgstr "Preset utente" msgid "Project Inside Preset" -msgstr "Project Inside Preset" +msgstr "Preset interno al Progetto" msgid "Name is invalid;" -msgstr "Name is invalid;" +msgstr "Nome non valido" msgid "illegal characters:" -msgstr "Illegal characters:" +msgstr "Caratteri illegali:" msgid "illegal suffix:" -msgstr "Illegal suffix:" +msgstr "Suffisso illegale:" msgid "Name is unavailable." -msgstr "Name is unavailable." +msgstr "Nome non disponibile" msgid "Overwrite a system profile is not allowed" -msgstr "Overwriting a system profile is not allowed." +msgstr "Non è consentito sovrascrivere un profilo di sistema." #, boost-format msgid "Preset \"%1%\" already exists." -msgstr "Preset \"%1%\" already exists." +msgstr "Preset \"%1%\" esiste già." #, boost-format msgid "Preset \"%1%\" already exists and is incompatible with current printer." msgstr "" -"Preset \"%1%\" already exists and is incompatible with the current printer." +"Preset \"%1%\" esiste già ma è incompatibile con la stampante corrente." msgid "Please note that saving action will replace this preset" -msgstr "Please note that saving will overwrite the current preset." +msgstr "Tieni presente che il salvataggio sovrascriverà il preset corrente." msgid "The name is not allowed to be empty." -msgstr "The name field is not allowed to be empty." +msgstr "Il campo nome non può essere vuoto." msgid "The name is not allowed to start with space character." -msgstr "The name is not allowed to start with a space." +msgstr "Il nome non può iniziare con uno spazio." msgid "The name is not allowed to end with space character." -msgstr "The name is not allowed to end with a space." +msgstr "Il nome non può terminare con uno spazio." msgid "The name cannot be the same as a preset alias name." msgstr "Il nome non può essere uguale a quello di un preset." @@ -5380,89 +5512,89 @@ msgstr "Copia" #, boost-format msgid "Printer \"%1%\" is selected with preset \"%2%\"" -msgstr "Printer \"%1%\" is selected with preset \"%2%\"" +msgstr "La stampante \"%1%\" è selezionata con il preset \"%2%\"" #, boost-format msgid "Please choose an action with \"%1%\" preset after saving." -msgstr "Please choose an action with \"%1%\" preset after saving." +msgstr "Scegli un'azione con \"%1%\" preimpostata dopo il salvataggio." #, boost-format msgid "For \"%1%\", change \"%2%\" to \"%3%\" " -msgstr "For \"%1%\", change \"%2%\" to \"%3%\" " +msgstr "Per \"%1%\", cambia \"%2%\" con \"%3%\"." #, boost-format msgid "For \"%1%\", add \"%2%\" as a new preset" -msgstr "For \"%1%\", add \"%2%\" as a new preset" +msgstr "Per \"%1%\", aggiungere \"%2%\" come nuovo preset" #, boost-format msgid "Simply switch to \"%1%\"" -msgstr "Simply switch to \"%1%\"" +msgstr "Basta passare a \"%1%\"" msgid "Task canceled" -msgstr "Task canceled" +msgstr "Attività annullata" msgid "(LAN)" msgstr "(LAN)" msgid "My Device" -msgstr "My Device" +msgstr "Mio dispositivo" msgid "Other Device" -msgstr "Other Device" +msgstr "Altro dispositivo" msgid "Online" msgstr "Online" msgid "Input access code" -msgstr "Input access code" +msgstr "Inserisci codice di accesso" msgid "Can't find my devices?" msgstr "Non riesci a trovare i dispositivi?" msgid "Log out successful." -msgstr "Log out successful." +msgstr "Log out riuscito." msgid "Offline" msgstr "Offline" msgid "Busy" -msgstr "Busy" +msgstr "Occupato" msgid "Bambu Cool Plate" msgstr "Bambu Cool Plate" msgid "PLA Plate" -msgstr "PLA Plate" +msgstr "Piastra PLA" msgid "Bambu Engineering Plate" -msgstr "" +msgstr "Bambu Engineering Plate" msgid "Bambu Smooth PEI Plate" -msgstr "" +msgstr "Bambu Smooth PEI Plate" msgid "High temperature Plate" -msgstr "" +msgstr "High Temperature Plate" msgid "Bambu Textured PEI Plate" -msgstr "" +msgstr "Bambu Textured PEI Plate" msgid "Send print job to" -msgstr "Send print job to" +msgstr "Invia stampa a" msgid "Refresh" -msgstr "Refresh" +msgstr "Aggiorna" msgid "Bed Leveling" -msgstr "Bed leveling" +msgstr "Livellamento piano" msgid "Flow Dynamics Calibration" -msgstr "" +msgstr "Calibrazione della dinamica del flusso" msgid "Click here if you can't connect to the printer" -msgstr "" +msgstr "Clicca qui se non puoi connetterti alla stampante" msgid "send completed" -msgstr "Send complete" +msgstr "Invio completo" msgid "Error code" msgstr "Codice di errore" @@ -5474,16 +5606,18 @@ msgid "Printer local connection failed, please try again." msgstr "Connessione locale della stampante fallita; Si prega di riprovare." msgid "No login account, only printers in LAN mode are displayed" -msgstr "No login account, only printers in LAN mode are displayed" +msgstr "" +"Nessun account di login, vengono visualizzate solo le stampanti in modalità " +"LAN" msgid "Connecting to server" -msgstr "Connecting to server" +msgstr "Connessione in corso al server" msgid "Synchronizing device information" -msgstr "Synchronizing device information" +msgstr "Sincronizzazione informazioni dispositivo" msgid "Synchronizing device information time out" -msgstr "Synchronizing device information timed out" +msgstr "La sincronizzazione informazioni dispositivo è scaduta" msgid "Cannot send the print job when the printer is updating firmware" msgstr "" @@ -5492,11 +5626,10 @@ msgstr "" msgid "" "The printer is executing instructions. Please restart printing after it ends" -msgstr "" -"The printer is executing instructions. Please restart printing after it ends" +msgstr "La stampante sta eseguendo le istruzioni. Riavvia la stampa al termine" msgid "The printer is busy on other print job" -msgstr "The printer is busy with another print job." +msgstr "La stampante è occupata con altro lavoro di stampa." #, c-format, boost-format msgid "" @@ -5524,8 +5657,8 @@ msgid "" "Please click each filament above to specify its mapping AMS slot before " "sending the print job" msgstr "" -"Please click each filament above to specify its mapping AMS slot before " -"sending the print job" +"Fai clic su ciascun filamento in alto per specificare lo slot AMS di " +"mappatura prima di inviare il lavoro di stampa" #, c-format, boost-format msgid "" @@ -5554,6 +5687,7 @@ msgstr "È necessario inserire una scheda microSD prima di stampare." msgid "The selected printer is incompatible with the chosen printer presets." msgstr "" +"La stampante selezionata è incompatibile con i preset della stampante scelti." msgid "An SD card needs to be inserted to record timelapse." msgstr "È necessario inserire una scheda microSD per registrare un timelapse." @@ -5575,11 +5709,15 @@ msgid "" "When enable spiral vase mode, machines with I3 structure will not generate " "timelapse videos." msgstr "" +"Quando si abilita la modalità vaso a spirale, le macchine con struttura I3 " +"non genereranno video timelapse." msgid "" "When print by object, machines with I3 structure will not generate timelapse " "videos." msgstr "" +"Quando si stampa per oggetto, le macchine con struttura I3 non genereranno " +"video timelapse." msgid "Errors" msgstr "Errori" @@ -5617,12 +5755,14 @@ msgstr "" msgid "" "Connecting to the printer. Unable to cancel during the connection process." msgstr "" +"Collegamento alla stampante. Impossibile annullare durante il processo di " +"connessione." msgid "Preparing print job" -msgstr "Preparing print job" +msgstr "Preparazione lavoro di stampa" msgid "Abnormal print file data. Please slice again" -msgstr "Abnormal print file data. Please slice again" +msgstr "Dati file di stampa anormali. Eseguire nuovamente l'elaborazione" msgid "The name length exceeds the limit." msgstr "La lunghezza del nome supera il limite." @@ -5631,18 +5771,21 @@ msgid "" "Caution to use! Flow calibration on Textured PEI Plate may fail due to the " "scattered surface." msgstr "" +"Attenzione da usare! La calibrazione del flusso sulla piastra PEI " +"testurizzata può non riuscire a causa della superficie Rugosa." msgid "Automatic flow calibration using Micro Lidar" -msgstr "" +msgstr "Calibrazione automatica del flusso tramite Micro Lidar" msgid "Modifying the device name" -msgstr "Modifying the device name" +msgstr "Modifica nome del dispositivo" msgid "Send to Printer SD card" msgstr "Invia a microSD stampante" msgid "Cannot send the print task when the upgrade is in progress" -msgstr "Cannot send print tasks when an update is in progress" +msgstr "" +"Impossibile inviare attività di stampa quando è in corso un aggiornamento" msgid "An SD card needs to be inserted before send to printer SD card." msgstr "" @@ -5680,13 +5823,13 @@ msgid "Receive login report timeout" msgstr "Timeout ricezione del rapporto di login" msgid "Unknown Failure" -msgstr "Unknown Failure" +msgstr "Fallimento sconosciuto" msgid "Log in printer" -msgstr "Log in printer" +msgstr "Log in stampante" msgid "Would you like to log in this printer with current account?" -msgstr "Would you like to log in this printer with the current account?" +msgstr "Vuoi accedere alla stampante con l'account corrente?" msgid "Check the reason" msgstr "Verifica la causa" @@ -5756,22 +5899,22 @@ msgid "Statement on User Experience Improvement Plan" msgstr "Dichiarazione del piano miglioramento dell'esperienza utente" msgid "Log in successful." -msgstr "Log in successful." +msgstr "Log in effettuato con successo." msgid "Log out printer" -msgstr "Log out printer" +msgstr "Log out dalla stampante" msgid "Would you like to log out the printer?" -msgstr "Would you like to log out the printer?" +msgstr "Vuoi disconnettere la stampante?" msgid "Please log in first." -msgstr "Please log in first." +msgstr "Prima effettua il login." msgid "There was a problem connecting to the printer. Please try again." -msgstr "There was a problem connecting to the printer. Please try again." +msgstr "Si è verificato un problema di connessione alla stampante. Riprovare." msgid "Failed to log out." -msgstr "Failed to log out." +msgstr "Disconnessione non riuscita." #. TRN "Save current Settings" #, c-format, boost-format @@ -5846,8 +5989,8 @@ msgstr "" 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\"." +"by right-click the empty position of build plate and choose \"Add " +"Primitive\"->\"Timelapse Wipe Tower\"." msgstr "" "Quando si registra un timelapse senza testa di satmpa, si consiglia di " "aggiungere un \"Timelapse Torre di pulizia\"\n" @@ -5855,70 +5998,71 @@ msgstr "" "piatto e scegli \"Aggiungi primitiva\" ->\"Timelapse Torre di pulizia\"»." msgid "Line width" -msgstr "Line width" +msgstr "Larghezza linea" msgid "Seam" -msgstr "Seam" +msgstr "Cucitura" msgid "Precision" -msgstr "Precision" +msgstr "Precisione" msgid "Wall generator" msgstr "Generatore parete" msgid "Walls" -msgstr "Walls" +msgstr "Pareti" msgid "Top/bottom shells" -msgstr "Top/bottom shells" +msgstr "Gusci superiori/inferiori" msgid "Initial layer speed" -msgstr "First layer speed" +msgstr "Velocità primo layer" msgid "Other layers speed" -msgstr "Other layers speed" +msgstr "Velocità altri layer" msgid "Overhang speed" -msgstr "Overhang speed" +msgstr "Velocità di sbalzo" msgid "" "This is the speed for various overhang degrees. Overhang degrees are " "expressed as a percentage of line width. 0 speed means no slowing down for " "the overhang degree range and wall speed is used" msgstr "" -"This is the speed for various overhang degrees. Overhang degrees are " -"expressed as a percentage of line width. 0 speed means no slowing down for " -"the overhang degree range and wall speed is used" +"È la velocità per vari gradi di sporgenza. I gradi di sporgenza sono " +"espressi come percentuale della larghezza della linea. Velocità 0 significa " +"che non c'è rallentamento per l'intervallo di gradi di sporgenza e viene " +"utilizzata la velocità della parete." msgid "Bridge" -msgstr "Bridge" +msgstr "Ponte" msgid "Set speed for external and internal bridges" -msgstr "" +msgstr "Impostare la velocità per ponti esterni e interni" msgid "Travel speed" -msgstr "Travel speed" +msgstr "Velocità spostamento" msgid "Acceleration" -msgstr "Acceleration" +msgstr "Accelerazione" msgid "Jerk(XY)" -msgstr "" +msgstr "Scatto (XY)" msgid "Raft" msgstr "Raft" msgid "Support filament" -msgstr "Filament for Supports" +msgstr "Filamento per supporti" msgid "Tree supports" -msgstr "" +msgstr "Supporti ad albero" msgid "Prime tower" msgstr "Prime tower" msgid "Special mode" -msgstr "Special mode" +msgstr "Modalità speciale" msgid "G-code output" msgstr "Uscita G-code" @@ -5927,10 +6071,10 @@ msgid "Post-processing Scripts" msgstr "Script post-elaborazione" msgid "Notes" -msgstr "" +msgstr "Note" msgid "Frequent" -msgstr "Frequent" +msgstr "Frequente" #, c-format, boost-format msgid "" @@ -5942,43 +6086,45 @@ msgid_plural "" "Please remove them, or will beat G-code visualization and printing time " "estimation." msgstr[0] "" -"Following line %s contains reserved keywords.\n" -"Please remove it, or G-code visualization and print time estimation will be " -"broken." +"La riga seguente %s contiene parole chiave riservata.\n" +"Rimuovilo, altrimenti la visualizzazione G-code e la stima del tempo di " +"stampa verranno interrotte." msgstr[1] "" "Following lines %s contain reserved keywords.\n" "Please remove them, or G-code visualization and print time estimation will " "be broken." msgid "Reserved keywords found" -msgstr "Reserved keywords found" +msgstr "Parole chiave riservate trovate" msgid "Setting Overrides" -msgstr "Setting Overrides" +msgstr "Sovrascrivi impostazioni" msgid "Retraction" msgstr "Retrazione" msgid "Basic information" -msgstr "Basic information" +msgstr "Informazioni di Base" msgid "Recommended nozzle temperature" -msgstr "Recommended nozzle temperature" +msgstr "Temperatura nozzle consigliata" msgid "Recommended nozzle temperature range of this filament. 0 means no set" -msgstr "Recommended nozzle temperature range of this filament. 0 means not set" +msgstr "" +"Intervallo di temperatura del nozzle consigliato per questo filamento. 0 " +"significa non impostato" msgid "Print chamber temperature" -msgstr "" +msgstr "Temperatura della camera di stampa" msgid "Print temperature" -msgstr "Print temperature" +msgstr "Temperatura stampa" msgid "Nozzle" msgstr "Ugello" msgid "Nozzle temperature when printing" -msgstr "Nozzle temperature when printing" +msgstr "Temperatura del nozzle durante la stampa" msgid "Cool plate" msgstr "Cool plate" @@ -5987,8 +6133,8 @@ msgid "" "Bed temperature when cool plate is installed. Value 0 means the filament " "does not support to print on the Cool Plate" msgstr "" -"This is the bed temperature when the cool plate is installed. A value of 0 " -"means the filament does not support printing on the Cool Plate." +"Temperatura del piano quando è installato il piatto Cool Plate. Il valore 0 " +"significa che il filamento non supporta la stampa su piatto Cool Plate." msgid "Engineering plate" msgstr "Engineering plate" @@ -5997,17 +6143,20 @@ msgid "" "Bed temperature when engineering plate is installed. Value 0 means the " "filament does not support to print on the Engineering Plate" msgstr "" -"This is the bed temperature when the engineering plate is installed. A value " -"of 0 means the filament does not support printing on the Engineering Plate." +"Temperatura del piano quando è installato il piatto Engineering. Il valore 0 " +"significa che il filamento non supporta la stampa su piatto Engineering." msgid "Smooth PEI Plate / High Temp Plate" -msgstr "" +msgstr "Piastra PEI liscia / piastra ad alta temperatura" msgid "" "Bed temperature when Smooth PEI Plate/High temperature plate is installed. " "Value 0 means the filament does not support to print on the Smooth PEI Plate/" "High Temp Plate" msgstr "" +"Temperatura del letto quando è installata la piastra PEI liscia/piastra ad " +"alta temperatura. Il valore 0 indica che il filamento non supporta la stampa " +"sulla piastra PEI liscia/piastra ad alta temperatura" msgid "Textured PEI Plate" msgstr "Textured PEI Plate" @@ -6020,19 +6169,19 @@ msgstr "" "0 significa che il filamento non è supportato sul piatto Textured PEI" msgid "Volumetric speed limitation" -msgstr "Volumetric speed limitation" +msgstr "Limitazione velocità volumetrica" msgid "Cooling" msgstr "Raffreddamento" msgid "Cooling for specific layer" -msgstr "Cooling for specific layer" +msgstr "Raffreddamento per specifico layer" msgid "Part cooling fan" -msgstr "Part cooling fan" +msgstr "Ventola di raffreddamento oggetto" msgid "Min fan speed threshold" -msgstr "Min fan speed threshold" +msgstr "Soglia minima velocità della ventola" msgid "" "Part cooling fan speed will start to run at min speed when the estimated " @@ -6040,68 +6189,69 @@ msgid "" "shorter than threshold, fan speed is interpolated between the minimum and " "maximum fan speed according to layer printing time" msgstr "" -"The part cooling fan will run at the minimum fan speed when the estimated " -"layer time is longer than the threshold value. When the layer time is " -"shorter than the threshold, the fan speed will be interpolated between the " -"minimum and maximum fan speed according to layer printing time." +"La ventola di raffreddamento della parte funzionerà alla velocità minima " +"della ventola quando la durata stimata del layer è superiore al valore di " +"soglia. Quando il tempo del layer è inferiore alla soglia, la velocità della " +"ventola verrà interpolata tra la velocità minima e massima della ventola in " +"base al tempo di stampa a layer." msgid "Max fan speed threshold" -msgstr "Max fan speed threshold" +msgstr "Soglia massima velocità della ventola" msgid "" "Part cooling fan speed will be max when the estimated layer time is shorter " "than the setting value" msgstr "" -"The part cooling fan will run at maximum speed when the estimated layer time " -"is shorter than the threshold value." +"La ventola di raffreddamento funzionerà alla massima velocità quando il " +"tempo layer stimato è inferiore al valore di soglia." msgid "Auxiliary part cooling fan" msgstr "Auxiliary part cooling fan" msgid "Exhaust fan" -msgstr "" +msgstr "Aspiratore" msgid "During print" -msgstr "" +msgstr "Durante la stampa" msgid "Complete print" -msgstr "" +msgstr "Stampa completa" msgid "Filament start G-code" -msgstr "Filament start G-code" +msgstr "G-code avvio filamento" msgid "Filament end G-code" msgstr "Filament end G-code" msgid "Multimaterial" -msgstr "" +msgstr "Apertura pittura multimateriale" msgid "Wipe tower parameters" -msgstr "" +msgstr "Parametri torre di pulitura" msgid "Toolchange parameters with single extruder MM printers" -msgstr "" +msgstr "Parametri di cambio strumento per stampanti MM con estrusore singolo" msgid "Ramming settings" -msgstr "" +msgstr "Impostazioni del ramming" msgid "Toolchange parameters with multi extruder MM printers" -msgstr "" +msgstr "Parametri di cambio strumento con stampanti MM multiestrusore" msgid "Printable space" -msgstr "Printable space" +msgstr "Spazio di stampa" msgid "Cooling Fan" -msgstr "" +msgstr "Velocità minima ventola di raffreddamento" msgid "Fan speed-up time" -msgstr "" +msgstr "Tempo di accelerazione della ventola" msgid "Extruder Clearance" msgstr "Spazio estrusore" msgid "Accessory" -msgstr "Accessory" +msgstr "Accessori" msgid "Machine gcode" msgstr "Machine G-code" @@ -6119,13 +6269,13 @@ msgid "Layer change G-code" msgstr "Layer change G-code" msgid "Time lapse G-code" -msgstr "" +msgstr "Time lapse G-code" msgid "Change filament G-code" -msgstr "Change filament G-code" +msgstr "G-code cambio filamento" msgid "Change extrusion role G-code" -msgstr "" +msgstr "Modificare il codice G del ruolo di estrusione" msgid "Pause G-code" msgstr "Pause G-code" @@ -6134,58 +6284,62 @@ msgid "Template Custom G-code" msgstr "Modello G-code personalizzato" msgid "Motion ability" -msgstr "Motion ability" +msgstr "Capacità di movimento" msgid "Normal" msgstr "Normale" msgid "Speed limitation" -msgstr "Speed limitation" +msgstr "Limitazione velocità" msgid "Acceleration limitation" -msgstr "Acceleration limitation" +msgstr "Limita Accelerazione" msgid "Jerk limitation" -msgstr "Jerk limitation" +msgstr "Limitazione jerk" msgid "Single extruder multimaterial setup" -msgstr "" +msgstr "Configurazione multimateriale estrusore singolo" msgid "Wipe tower" -msgstr "" +msgstr "Torre di pulitura" msgid "Single extruder multimaterial parameters" -msgstr "" +msgstr "Parametri estrusore singolo materiale multiplo" msgid "Layer height limits" msgstr "Limiti altezza layer" msgid "Lift Z Enforcement" -msgstr "" +msgstr "Applicazione dell'ascensore Z" msgid "Retraction when switching material" -msgstr "Retraction when switching material" +msgstr "Retrazione quando si cambia materiale" msgid "" "The Wipe option is not available when using the Firmware Retraction mode.\n" "\n" "Shall I disable it in order to enable Firmware Retraction?" msgstr "" +"La funzione Pulitura non è disponibile quando si usa la modalità Retrazione " +"Firmware.\n" +"\n" +"Devo disattivarla per poter abilitare la Retrazione Firmware?" msgid "Firmware Retraction" -msgstr "" +msgstr "Retrazione Firmware" msgid "Detached" msgstr "Distaccato" msgid "Following preset will be deleted too." msgid_plural "Following presets will be deleted too." -msgstr[0] "The following preset will be deleted too:" +msgstr[0] "Verrà eliminato anche il seguente preset:" msgstr[1] "The following presets will be deleted too:" #, boost-format msgid "Are you sure to %1% the selected preset?" -msgstr "Are you sure you want to %1% the selected preset?" +msgstr "Sei sicuro di voler %1% il preset selezionato?" #. TRN Remove/Delete #, boost-format @@ -6199,22 +6353,25 @@ msgid "Set" msgstr "Imposta" msgid "Click to reset current value and attach to the global value." -msgstr "Click to reset current value and attach to the global value." +msgstr "" +"Fare clic per ripristinare il valore corrente e associarlo al valore globale." msgid "Click to drop current modify and reset to saved value." -msgstr "Click to drop current modifications and reset to saved value." +msgstr "" +"Fai clic per eliminare le modifiche correnti e ripristinare il valore " +"salvato." msgid "Process Settings" -msgstr "Process Settings" +msgstr "Impostazioni processo" msgid "Undef" -msgstr "Undef" +msgstr "Indefinito" msgid "Unsaved Changes" msgstr "Modifiche non salvate" msgid "Discard or Keep changes" -msgstr "Discard or keep changes" +msgstr "Scarta o mantieni le modifiche" msgid "Old Value" msgstr "Valore precedente" @@ -6232,29 +6389,30 @@ msgid "Discard" msgstr "Cancella" msgid "Click the right mouse button to display the full text." -msgstr "Click the right mouse button to display the full text." +msgstr "" +"Clicca il pulsante destro del mouse per visualizzare il testo completo." msgid "All changes will not be saved" -msgstr "No changes will be saved." +msgstr "Nessuna modifica verrà salvata." msgid "All changes will be discarded." -msgstr "All changes will be discarded." +msgstr "Tutte le modifiche verranno eliminate." msgid "Save the selected options." msgstr "Salvare le opzioni selezionate." msgid "Keep the selected options." -msgstr "Keep the selected options." +msgstr "Mantieni le opzioni selezionate." msgid "Transfer the selected options to the newly selected preset." -msgstr "Transfer the selected options to the newly selected preset." +msgstr "Trasferisci le opzioni selezionate nel preset appena selezionato." #, boost-format msgid "" "Save the selected options to preset \n" "\"%1%\"." msgstr "" -"Save the selected options to preset \n" +"Salva le opzioni selezionate in un preset \n" "\"%1%\"." #, boost-format @@ -6262,28 +6420,28 @@ msgid "" "Transfer the selected options to the newly selected preset \n" "\"%1%\"." msgstr "" -"Transfer the selected options to the newly selected preset \n" +"Trasferisci le opzioni selezionate nel preset appena selezionato \n" "\"%1%\"." #, boost-format msgid "Preset \"%1%\" contains the following unsaved changes:" -msgstr "Preset \"%1%\" contains the following unsaved changes:" +msgstr "Preset \"%1%\" contiene modifiche non salvate:" #, boost-format msgid "" "Preset \"%1%\" is not compatible with the new printer profile and it " "contains the following unsaved changes:" msgstr "" -"Preset \"%1%\" is not compatible with the new printer profile and it " -"contains the following unsaved changes:" +"Preset \"%1%\" non compatibile con il nuovo profilo della stampante e " +"contiene modifiche non salvate:" #, boost-format msgid "" "Preset \"%1%\" is not compatible with the new process profile and it " "contains the following unsaved changes:" msgstr "" -"Preset \"%1%\" is not compatible with the new process profile and it " -"contains the following unsaved changes:" +"Preset \"%1%\" non compatibile con il nuovo profilo di processo e contiene " +"modifiche non salvate:" #, boost-format msgid "" @@ -6321,63 +6479,64 @@ msgid "Add File" msgstr "Aggiungi file" msgid "Set as cover" -msgstr "Set as cover" +msgstr "Impostare come copertina" msgid "Cover" msgstr "Cover" #, boost-format msgid "The name \"%1%\" already exists." -msgstr "The name \"%1%\" already exists." +msgstr "Il nome \"%1%\" già esiste." msgid "Basic Info" -msgstr "Basic Info" +msgstr "Info di base" msgid "Pictures" -msgstr "Pictures" +msgstr "Immagini" msgid "Bill of Materials" -msgstr "Bill of Materials" +msgstr "Lista materiali" msgid "Assembly Guide" -msgstr "Assembly Guide" +msgstr "Guida al montaggio" msgid "Author" msgstr "Autore" msgid "Model Name" -msgstr "Model Name" +msgstr "Nome modello" #, c-format, boost-format msgid "%s Update" msgstr "%s Update" msgid "A new version is available" -msgstr "A new version is available" +msgstr "Una nuova versione è disponibile." msgid "Configuration update" msgstr "Aggiornamento di configurazione" msgid "A new configuration package available, Do you want to install it?" -msgstr "A new configuration package is available. Do you want to install it?" +msgstr "È disponibile un nuovo pacchetto di configurazione. Vuoi installarlo?" msgid "Description:" -msgstr "Description:" +msgstr "Descrizione:" msgid "Configuration incompatible" msgstr "Configuration incompatible" msgid "the configuration package is incompatible with current application." msgstr "" -"the configuration package is incompatible with the current application." +"il pacchetto di configurazione non è compatibile con l'applicazione corrente." #, c-format, boost-format msgid "" "The configuration package is incompatible with current application.\n" "%s will update the configuration package, Otherwise it won't be able to start" msgstr "" -"The configuration package is incompatible with the current application.\n" -"%s will update the configuration package to allow the application to start." +"Pacchetto configurazione non compatibile con l'applicazione corrente.\n" +"%s aggiornerà il pacchetto configurazione per consentire l'avvio " +"dell'applicazione." #, c-format, boost-format msgid "Exit %s" @@ -6385,20 +6544,20 @@ msgstr "Chiudi %s" msgid "the Configuration package is incompatible with current APP." msgstr "" -"The configuration package is incompatible with the current version of Bambu " -"Studio." +"Il pacchetto di configurazione non è compatibile con la versione corrente di " +"Bambu Studio." msgid "Configuration updates" msgstr "Aggiornamenti di configurazione" msgid "No updates available." -msgstr "No updates available." +msgstr "Nessun aggiornamento disponibile." msgid "The configuration is up to date." -msgstr "The configuration is up to date." +msgstr "Configurazione aggiornata." msgid "Ramming customization" -msgstr "" +msgstr "Personalizzazione del ramming" msgid "" "Ramming denotes the rapid extrusion just before a tool change in a single-" @@ -6411,33 +6570,45 @@ msgid "" "This is an expert-level setting, incorrect adjustment will likely lead to " "jams, extruder wheel grinding into filament etc." msgstr "" +"Per Ramming si intende l'estrusione rapida appena prima di un cambio " +"strumento in una stampante MM con estrusore singolo. Il suo scopo è quello " +"di dare una forma corretta all'estremità del filamento scaricato, in modo " +"che non impedisca l'inserimento del nuovo filamento e possa essere " +"reinserito successivamente. Questa fase è importante e i diversi materiali " +"possono richiedere velocità di estrusione diverse per ottenere una buona " +"forma. Per questo motivo, le velocità di estrusione nella fase di ramming " +"sono regolabili.\n" +"\n" +"Si tratta di un'impostazione per esperti: una regolazione errata potrebbe " +"causare inceppamenti, la macinazione del filamento da parte dell'ingranaggio " +"dell'estrusore e così via." msgid "Total ramming time" -msgstr "" +msgstr "Durata totale di ramming" msgid "s" msgstr "s" msgid "Total rammed volume" -msgstr "" +msgstr "Volume totale di ramming" msgid "Ramming line width" -msgstr "" +msgstr "Larghezza della linea di Ramming" msgid "Ramming line spacing" -msgstr "" +msgstr "Spaziatura tra linee di ramming" msgid "Auto-Calc" -msgstr "Auto-Calc" +msgstr "Calcolo automatico" msgid "Flushing volumes for filament change" -msgstr "Flushing volumes for filament change" +msgstr "Volumi di spurgo per il cambio filamento" msgid "Multiplier" msgstr "Moltiplicatore" msgid "Flushing volume (mm³) for each filament pair." -msgstr "Flushing volume (mm³) for each filament pair." +msgstr "Volume di spurgo (mm³) per ogni coppia di filamento." #, c-format, boost-format msgid "Suggestion: Flushing Volume in range [%d, %d]" @@ -6454,28 +6625,30 @@ msgid "loaded" msgstr "caricato" msgid "Filament #" -msgstr "Filament #" +msgstr "Filamento #" msgid "From" msgstr "Da" msgid "To" -msgstr "To" +msgstr "A" msgid "Login" msgstr "Login" msgid "The configuration package is changed in previous Config Guide" -msgstr "The configuration package is changed in previous Config Guide" +msgstr "" +"Il pacchetto di configurazione è stato modificato nella precedente Guida " +"alla configurazione" msgid "Configuration package changed" -msgstr "Configuration package changed" +msgstr "Pacchetto di configurazione modificato" msgid "Toolbar" msgstr "Toolbar" msgid "Objects list" -msgstr "Objects list" +msgstr "Elenco oggetti" msgid "Import geometry data from STL/STEP/3MF/OBJ/AMF files" msgstr "Importa geometrie da file STL/STEP/3MF/OBJ/AMF." @@ -6501,7 +6674,7 @@ msgid "Show keyboard shortcuts list" msgstr "Mostra elenco scorciatoie di tastiera" msgid "Global shortcuts" -msgstr "Global shortcuts" +msgstr "Scorciatoie globali" msgid "Rotate View" msgstr "Ruota vista" @@ -6526,9 +6699,9 @@ msgid "" "objects, it just orientates the selected ones.Otherwise, it will orientates " "all objects in the current disk." msgstr "" -"This auto orients selected objects or all objects. If there are selected " -"objects, it just orients the selected ones. Otherwise, it will orient all " -"objects in the current plate." +"Questo orienta automaticamente gli oggetti selezionati o tutti gli oggetti. " +"Se ci sono oggetti selezionati, orienta solo quelli selezionati. Altrimenti, " +"orienterà tutti gli oggetti nel piatto corrente." msgid "Shift+Tab" msgstr "Shift+Tab" @@ -6552,7 +6725,7 @@ msgid "⌘+Left mouse button" msgstr "⌘+Tasto sinistro del mouse" msgid "Select multiple objects" -msgstr "Select multiple objects" +msgstr "Seleziona più oggetti" msgid "Ctrl+Any arrow" msgstr "Ctrl+qualsiasi freccia" @@ -6567,7 +6740,7 @@ msgid "Shift+Left mouse button" msgstr "Shift+tasto sinistro mouse" msgid "Select objects by rectangle" -msgstr "Select objects by rectangle" +msgstr "Seleziona oggetti per rettangolo" msgid "Arrow Up" msgstr "Freccia Su" @@ -6603,28 +6776,28 @@ msgid "Esc" msgstr "Esc" msgid "keyboard 1-9: set filament for object/part" -msgstr "Keyboard 1-9: set filament for object/part" +msgstr "Tastiera 1-9: imposta il filamento per l'oggetto/la parte" msgid "Camera view - Default" -msgstr "Camera view - Default" +msgstr "Vista telecamera - Default" msgid "Camera view - Top" -msgstr "Camera view - Top" +msgstr "Vista telecamera - In Alto" msgid "Camera view - Bottom" -msgstr "Camera view - Bottom" +msgstr "Vista telecamera - Basso" msgid "Camera view - Front" -msgstr "Camera view - Front" +msgstr "Vista telecamera - Fronte" msgid "Camera view - Behind" -msgstr "Camera view - Behind" +msgstr "Vista telecamera - Dietro" msgid "Camera Angle - Left side" -msgstr "Camera Angle - Left side" +msgstr "Angolo della camera - Lato sinistro" msgid "Camera Angle - Right side" -msgstr "Camera Angle - Right side" +msgstr "Angolo della camera - Lato destro" msgid "Select all objects" msgstr "Seleziona tutti gli oggetti" @@ -6651,25 +6824,25 @@ msgid "Gizmo FDM paint-on seam" msgstr "Gizmo FDM pittura giunzione" msgid "Swtich between Prepare/Prewview" -msgstr "" +msgstr "Swtich tra Prepare/Prewview" msgid "Plater" msgstr "Piano" msgid "Move: press to snap by 1mm" -msgstr "Move: press to snap by 1mm" +msgstr "Sposta: premi per muovere di 1 mm" msgid "⌘+Mouse wheel" msgstr "⌘+Rotella mouse" msgid "Support/Color Painting: adjust pen radius" -msgstr "Support/Color Painting: adjust pen radius" +msgstr "Supporto/Pittura a colori: regolare il raggio della penna" msgid "⌥+Mouse wheel" msgstr "⌥+Rotella mouse" msgid "Support/Color Painting: adjust section position" -msgstr "Support/Color Painting: adjust section position" +msgstr "Supporto/Pittura a colori: regolare la posizione della sezione" msgid "Ctrl+Mouse wheel" msgstr "Ctrl+Rotellina del mouse" @@ -6681,22 +6854,25 @@ msgid "Gizmo" msgstr "Gizmo" msgid "Set extruder number for the objects and parts" -msgstr "Set extruder number for the objects and parts" +msgstr "Imposta il numero estrusore per gli oggetti e le parti" msgid "Delete objects, parts, modifiers " -msgstr "Delete objects, parts, modifiers" +msgstr "Eliminare oggetti, parti, modificatori " msgid "Space" -msgstr "Space" +msgstr "Spazio" msgid "Select the object/part and press space to change the name" -msgstr "Select the object/part and press space to change the name" +msgstr "" +"Seleziona l'oggetto/la parte e premi la barra spaziatrice per cambiare il " +"nome" msgid "Mouse click" -msgstr "Mouse click" +msgstr "Clic del mouse" msgid "Select the object/part and mouse click to change the name" -msgstr "Select the object/part and mouse click to change the name" +msgstr "" +"Seleziona l'oggetto/la parte e fai clic con il mouse per modificare il nome" msgid "Objects List" msgstr "Elenco oggetti" @@ -6718,10 +6894,10 @@ msgid "On/Off one layer mode of the vertical slider" msgstr "On/Off modalità un layer del cursore di scorrimento verticale" msgid "On/Off g-code window" -msgstr "" +msgstr "Attivazione/disattivazione della finestra g-code" msgid "Move slider 5x faster" -msgstr "Move slider 5x faster" +msgstr "Sposta il cursore 5 volte più velocemente" msgid "Shift+Mouse wheel" msgstr "Shift+Rotella mouse" @@ -6826,8 +7002,8 @@ msgid "" msgstr "" "È stato rilevato un aggiornamento importante che deve essere eseguito prima " "che la stampa possa continuare. Si desidera aggiornare ora? È possibile " -"effettuare l'aggiornamento anche in un secondo momento da \"Aggiorna firmware" -"\"." +"effettuare l'aggiornamento anche in un secondo momento da \"Aggiorna " +"firmware\"." msgid "" "The firmware version is abnormal. Repairing and updating are required before " @@ -6846,59 +7022,60 @@ msgid "Saving objects into the 3mf failed." msgstr "Saving objects into the 3mf failed." msgid "Only Windows 10 is supported." -msgstr "Only Windows 10 is supported." +msgstr "È supportato solo Windows 10." msgid "Failed to initialize the WinRT library." -msgstr "Failed to initialize the WinRT library." +msgstr "Impossibile inizializzare la libreria WinRT." msgid "Exporting objects" -msgstr "Exporting objects" +msgstr "Esportazione oggetti" msgid "Failed loading objects." -msgstr "Failed loading objects." +msgstr "Caricamento oggetti fallito." msgid "Repairing object by Windows service" -msgstr "Repairing object by Windows service" +msgstr "Riparazione oggetto da parte del servizio Windows" msgid "Repair failed." -msgstr "Repair failed." +msgstr "Riparazione fallita." msgid "Loading repaired objects" -msgstr "Loading repaired objects" +msgstr "Caricamento oggetti riparati" msgid "Exporting 3mf file failed" -msgstr "Exporting 3mf file failed" +msgstr "Esportazione del file 3mf non riuscita" msgid "Import 3mf file failed" -msgstr "Import 3mf file failed" +msgstr "Importazione del file 3mf non riuscita" msgid "Repaired 3mf file does not contain any object" -msgstr "The repaired 3mf file does not contain any objects." +msgstr "Il file 3mf riparato non contiene alcun oggetto." msgid "Repaired 3mf file contains more than one object" -msgstr "The repaired 3mf file contains more than one object." +msgstr "Il file 3mf riparato contiene più di un oggetto." msgid "Repaired 3mf file does not contain any volume" -msgstr "The repaired 3mf file does not contain any volume." +msgstr "Il file 3mf riparato non contiene alcun volume." msgid "Repaired 3mf file contains more than one volume" -msgstr "The repaired 3mf file contains more than one volume." +msgstr "Il file 3mf riparato contiene più di un volume." msgid "Repair finished" -msgstr "Repair finished" +msgstr "Riparazione finita" msgid "Repair canceled" -msgstr "Repair canceled" +msgstr "Riparazione annullata" #, boost-format msgid "Copying of file %1% to %2% failed: %3%" msgstr "Copia del file %1% su %2% non riuscita: %3%" msgid "Need to check the unsaved changes before configuration updates." -msgstr "Please check any unsaved changes before updating the configuration." +msgstr "" +"Controllare le modifiche non salvate prima di aggiornare la configurazione." msgid "Configuration package updated to " -msgstr "Configuration package updated to " +msgstr "Pacchetto di configurazione aggiornato a " msgid "Open G-code file:" msgstr "Apri un file G-code:" @@ -6907,88 +7084,96 @@ msgid "" "One object has empty initial layer and can't be printed. Please Cut the " "bottom or enable supports." msgstr "" -"One object has empty initial layer and can't be printed. Please Cut the " -"bottom or enable supports." +"Un oggetto ha un livello iniziale vuoto e non può essere stampato. Taglia il " +"fondo o abilita i supporti." #, boost-format msgid "Object can't be printed for empty layer between %1% and %2%." -msgstr "The object has empty layers between %1% and %2% and can’t be printed." +msgstr "" +"L'oggetto ha layer vuoti compresi tra %1% e %2% e non può essere stampato." #, boost-format msgid "Object: %1%" -msgstr "Object: %1%" +msgstr "Oggetto: %1%" msgid "" "Maybe parts of the object at these height are too thin, or the object has " "faulty mesh" msgstr "" -"Parts of the object at these heights may be too thin or the object may have " -"a faulty mesh." +"Le parti dell'oggetto a queste altezze potrebbero essere troppo sottili o " +"l'oggetto potrebbe avere una mesh difettosa." msgid "No object can be printed. Maybe too small" -msgstr "No object can be printed. It may be too small." +msgstr "" +"Non è possibile stampare alcun oggetto. Potrebbe essere troppo piccolo." msgid "" "Failed to generate gcode for invalid custom G-code.\n" "\n" msgstr "" -"Failed to generate G-code for invalid custom G-code.\n" +"Impossibile generare il G-code per G-code personalizzato non valido.\n" "\n" msgid "Please check the custom G-code or use the default custom G-code." -msgstr "Please check the custom G-code or use the default custom G-code." +msgstr "" +"Controlla il G-code personalizzato o utilizza il G-code personalizzato " +"predefinito." #, boost-format msgid "Generating G-code: layer %1%" -msgstr "Generating G-code: layer %1%" +msgstr "Genera G-code: layer %1%" msgid "Inner wall" -msgstr "Inner wall" +msgstr "Parete interna" msgid "Outer wall" -msgstr "Outer wall" +msgstr "Parete esterna" msgid "Overhang wall" -msgstr "Overhang wall" +msgstr "Parete a sbalzo" msgid "Sparse infill" -msgstr "Sparse infill" +msgstr "Riempimento" msgid "Internal solid infill" -msgstr "Internal solid infill" +msgstr "Riempimento solido interno" msgid "Top surface" -msgstr "Top surface" +msgstr "Superficie superiore" msgid "Bottom surface" -msgstr "Bottom surface" +msgstr "Superficie inferiore" msgid "Internal Bridge" -msgstr "" +msgstr "Ponte interno" msgid "Gap infill" -msgstr "Gap infill" +msgstr "Riempimento gap" msgid "Skirt" -msgstr "" +msgstr "Skirt" msgid "Support interface" -msgstr "Support interface" +msgstr "Interfaccia supporto" msgid "Support transition" -msgstr "Support transition" +msgstr "Supporto alla transizione" msgid "Multiple" -msgstr "Multiple" +msgstr "Multiplo" #, boost-format msgid "Failed to calculate line width of %1%. Can not get value of \"%2%\" " -msgstr "Failed to calculate line width of %1%. Cannot get value of “%2%” " +msgstr "" +"Impossibile calcolare la larghezza della linea di %1%. Impossibile ottenere " +"il valore \"%2%\" " msgid "" "Invalid spacing supplied to Flow::with_spacing(), check your layer height " "and extrusion width" msgstr "" +"Spaziatura non valida fornita a Flow::with_spacing (), controlla l'altezza " +"del livello e la larghezza di estrusione" msgid "undefined error" msgstr "errore non definito" @@ -7015,22 +7200,22 @@ msgid "not a ZIP archive" msgstr "non un archivio ZIP" msgid "invalid header or corrupted" -msgstr "invalid header or corrupted" +msgstr "intestazione non valida o danneggiata" msgid "unsupported multidisk" -msgstr "Saving to RAID is not supported." +msgstr "Il salvataggio su RAID non supportato." msgid "decompression failed" -msgstr "decompression failed" +msgstr "decompressione fallita" msgid "compression failed" -msgstr "compressione fallita" +msgstr "Compressione non riuscita" msgid "unexpected decompressed size" msgstr "dimensione decompressa imprevista" msgid "CRC check failed" -msgstr "CRC check failed" +msgstr "Controllo CRC non riuscito" msgid "unsupported central directory size" msgstr "dimensione della directory centrale non supportata" @@ -7075,13 +7260,13 @@ msgid "file not found" msgstr "file non trovato" msgid "archive too large" -msgstr "Archive too large" +msgstr "Archivio troppo grande" msgid "validation failed" msgstr "convalida non riuscita" msgid "write callback failed" -msgstr "write callback failed" +msgstr "scrittura callback fallita" #, boost-format msgid "" @@ -7092,11 +7277,11 @@ msgstr "" #, boost-format msgid "%1% is too close to others, and collisions may be caused." -msgstr "%1% is too close to others, and collisions may be caused." +msgstr "%1% è troppo vicino ad altri oggetti e potrebbe causare collisioni." #, boost-format msgid "%1% is too tall, and collisions will be caused." -msgstr "%1% is too tall, and collisions will be caused." +msgstr "%1% è troppo alto e si verificheranno collisioni." msgid " is too close to others, there may be collisions when printing." msgstr "" @@ -7112,22 +7297,23 @@ msgid "Prime Tower" msgstr "Prime Tower" msgid " is too close to others, and collisions may be caused.\n" -msgstr " is too close to others, and collisions may be caused.\n" +msgstr " è troppo vicino agli altri e possono verificarsi collisioni.\n" msgid " is too close to exclusion area, and collisions will be caused.\n" -msgstr " is too close to an exclusion area, and collisions will be caused.\n" +msgstr "" +" è troppo vicino a un'area di esclusione e si verificheranno collisioni.\n" msgid "" "Can not print multiple filaments which have large difference of temperature " "together. Otherwise, the extruder and nozzle may be blocked or damaged " "during printing" msgstr "" -"Unable to print multiple filaments which have large temperature differences " -"together. Otherwise, the extruder and nozzle may be blocked or damaged " -"during printing." +"Impossibile stampare filamenti che presentano grandi differenze di " +"temperatura insieme. In caso contrario, l'estrusore e il nozzle potrebbero " +"bloccarsi o danneggiarsi durante la stampa." msgid "No extrusions under current settings." -msgstr "No extrusions under current settings." +msgstr "Nessuna estrusione con le impostazioni attuali." msgid "" "Smooth mode of timelapse is not supported when \"by object\" sequence is " @@ -7140,64 +7326,71 @@ msgid "" "Please select \"By object\" print sequence to print multiple objects in " "spiral vase mode." msgstr "" -"Please select \"By object\" print sequence to print multiple objects in " -"spiral vase mode." +"Seleziona la sequenza di stampa \"Per oggetto\" per stampare più oggetti in " +"modalità vaso a spirale." msgid "" "The spiral vase mode does not work when an object contains more than one " "materials." msgstr "" -"Spiral (vase) mode does not work when an object contains more than one " -"material." +"La modalità Spirale (vaso) non funziona quando un oggetto contiene più di un " +"materiale." #, boost-format msgid "The object %1% exceeds the maximum build volume height." -msgstr "" +msgstr "L'oggetto %1% supera l'altezza massima del volume di stampa." #, boost-format msgid "" "While the object %1% itself fits the build volume, its last layer exceeds " "the maximum build volume height." msgstr "" +"Sebbene l'oggetto %1% rientri nel volume di stampa, il suo ultimo layer " +"supera l'altezza massima." msgid "" "You might want to reduce the size of your model or change current print " "settings and retry." msgstr "" +"È possibile ridurre le dimensioni del modello o modificare le impostazioni " +"di stampa correnti e riprovare." msgid "Variable layer height is not supported with Organic supports." -msgstr "" +msgstr "Layer ad altezza variabile non è compatibile con i Supporti Organici." msgid "The prime tower is not supported in \"By object\" print." -msgstr "A prime tower is not supported in “By object” print." +msgstr "La Prime Tower non è supportata nella stampa \"Per oggetto\"." msgid "" "The prime tower is not supported when adaptive layer height is on. It " "requires that all objects have the same layer height." msgstr "" -"A prime tower is not supported when adaptive layer height is on. It requires " -"that all objects have the same layer height." +"La Prime Tower non è supportata quando è attivo Layer adattativi. Richiede " +"che tutti gli oggetti abbiano la stessa altezza layer." msgid "The prime tower requires \"support gap\" to be multiple of layer height" msgstr "" -"A prime tower requires any “support gap” to be a multiple of layer height." +"La Prime Tower richiede che il \"gap supporto\" sia un multiplo dell'altezza " +"del layer." msgid "The prime tower requires that all objects have the same layer heights" -msgstr "A prime tower requires that all objects have the same layer height." +msgstr "" +"La Prime Tower richiede che tutti gli oggetti abbiano la stessa altezza " +"layer." msgid "" "The prime tower requires that all objects are printed over the same number " "of raft layers" msgstr "" -"A prime tower requires that all objects are printed over the same number of " -"raft layers." +"La Prime Tower richiede che tutti gli oggetti siano stampati sullo stesso " +"numero di layers del raft." msgid "" "The prime tower requires that all objects are sliced with the same layer " "heights." msgstr "" -"A prime tower requires that all objects are sliced with the same layer " -"height." +"La Prime Tower richiede che tutti gli oggetti siano elaborati con la stessa " +"altezza layer." msgid "" "The prime tower is only supported if all objects have the same variable " @@ -7207,64 +7400,79 @@ msgstr "" "altezza layer adattativi." msgid "Too small line width" -msgstr "Line width too small" +msgstr "Larghezza linea troppo piccola" msgid "Too large line width" -msgstr "Line width too large" +msgstr "Larghezza linea troppo grande" msgid "" "The prime tower requires that support has the same layer height with object." msgstr "" -"A prime tower requires that support has the same layer height as the object." +"La Prime Tower richiede che il supporto abbia la stessa altezza layer " +"dell'oggetto." msgid "" "Organic support tree tip diameter must not be smaller than support material " "extrusion width." msgstr "" +"Il diametro della punta del supporto organico non deve essere inferiore alla " +"larghezza dell'estrusione del materiale di supporto." msgid "" "Organic support branch diameter must not be smaller than 2x support material " "extrusion width." msgstr "" +"Il diametro della ramificazione del supporto organico non deve essere minore " +"di 2 volte rispetto alla larghezza dell'estrusione del materiale di supporto." msgid "" "Organic support branch diameter must not be smaller than support tree tip " "diameter." msgstr "" +"Il diametro della ramificazione organica non deve essere inferiore al " +"diametro della punta del supporto ad albero." msgid "" "Support enforcers are used but support is not enabled. Please enable support." msgstr "" -"Support enforcers are used but support is not enabled. Please enable support." +"Utilizzati supporti forzati ma i supporti non sono abilitati. Abilitare i " +"supporti." msgid "Layer height cannot exceed nozzle diameter" -msgstr "Layer height cannot exceed nozzle diameter." +msgstr "L'altezza del layer non può superare il diametro del nozzle." msgid "" "Relative extruder addressing requires resetting the extruder position at " "each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to " "layer_gcode." msgstr "" +"L'indirizzamento relativo dell'estrusore richiede la reimpostazione della " +"posizione dell'estrusore ad ogni strato per evitare la perdita di precisione " +"in virgola mobile. Aggiungi \"G92 E0\" a layer_gcode." msgid "" "\"G92 E0\" was found in before_layer_gcode, which is incompatible with " "absolute extruder addressing." msgstr "" +"\"G92 E0\" trovato in before_layer_gcode, che è incompatibile con " +"l'indirizzamento assoluto dell'estrusore." msgid "" "\"G92 E0\" was found in layer_gcode, which is incompatible with absolute " "extruder addressing." msgstr "" +"\"G92 E0\" trovato in layer_gcode, che è incompatibile con l'indirizzamento " +"assoluto dell'estrusore." #, c-format, boost-format msgid "Plate %d: %s does not support filament %s" msgstr "Piatto %d: %s non supporta il filamento %s" msgid "Generating skirt & brim" -msgstr "Generating skirt & brim" +msgstr "Generazione skirt & brim" msgid "Exporting G-code" -msgstr "Esportando il G-code" +msgstr "Esportazione G-code" msgid "Generating G-code" msgstr "Generazione G-code" @@ -7276,7 +7484,7 @@ msgid "Printable area" msgstr "Printable area" msgid "Bed exclude area" -msgstr "Excluded bed area" +msgstr "Zona piano esclusa" msgid "" "Unprintable area in XY plane. For example, X1 Series printers use the front " @@ -7301,11 +7509,11 @@ msgid "" "Shrink the initial layer on build plate to compensate for elephant foot " "effect" msgstr "" -"This shrinks the first layer on the build plate to compensate for elephant " -"foot effect." +"Questo restringe il primo layer sulla piatto per compensare l'effetto zampa " +"d'elefante." msgid "Elephant foot compensation layers" -msgstr "" +msgstr "Layer di compensazione del piede elefante" msgid "" "The number of layers on which the elephant foot compensation will be active. " @@ -7313,6 +7521,10 @@ msgid "" "the next layers will be linearly shrunk less, up to the layer indicated by " "this value." msgstr "" +"Il numero di strati su cui sarà attivo la compensazione del piede degli " +"elefanti. Il primo strato verrà ridotto dal valore di compensazione del " +"piede degli elefanti, quindi gli strati successivi saranno ridotti in modo " +"linearmente ridotto, fino allo strato indicato da questo valore." msgid "layers" msgstr "layer" @@ -7321,16 +7533,16 @@ msgid "" "Slicing height for each layer. Smaller layer height means more accurate and " "more printing time" msgstr "" -"This is the height for each layer. Smaller layer heights give greater " -"accuracy but longer printing time." +"Questa è l'altezza di ogni layer. Le altezze dei layers inferiori offrono " +"una maggiore precisione ma tempi di stampa più lunghi." msgid "Printable height" -msgstr "Printable height" +msgstr "Altezza di stampa" msgid "Maximum printable height which is limited by mechanism of printer" msgstr "" -"This is the maximum printable height which is limited by the height of the " -"build area." +"È l'altezza massima stampabile, limitata dall'altezza dell'area di " +"costruzione." msgid "Printer preset names" msgstr "Nomi dei preset della stampante" @@ -7352,11 +7564,13 @@ msgstr "" "seguente formato: https://username:password@your-octopi-address/" msgid "Device UI" -msgstr "" +msgstr "Interfaccia utente del dispositivo" msgid "" "Specify the URL of your device user interface if it's not same as print_host" msgstr "" +"Specificare l'URL dell'interfaccia utente del dispositivo se non corrisponde " +"a print_host" msgid "API Key / Password" msgstr "Chiave API / Password" @@ -7418,8 +7632,8 @@ msgstr "Evita di attraversare le pareti" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" -"This detours to avoid traveling across walls, which may cause blobs on the " -"surface" +"Questa deviazione evita di attraversare le pareti, il che può causare la " +"formazione di bolle sulla superficie." msgid "Avoid crossing wall - Max detour length" msgstr "Evitare di attraversare le pareti - Lunghezza massima della deviazione" @@ -7446,8 +7660,8 @@ msgid "" "Bed temperature for layers except the initial one. Value 0 means the " "filament does not support to print on the Cool Plate" msgstr "" -"This is the bed temperature for layers except for the first one. A value of " -"0 means the filament does not support printing on the Cool Plate." +"Questa è la temperatura del piano per i layer (tranne il primo). Un valore " +"pari a 0 indica che il filamento non supporta la stampa sul Cool Plate." msgid "°C" msgstr "°C" @@ -7456,15 +7670,16 @@ msgid "" "Bed temperature for layers except the initial one. Value 0 means the " "filament does not support to print on the Engineering Plate" msgstr "" -"This is the bed temperature for layers except for the first one. A value of " -"0 means the filament does not support printing on the Engineering Plate." +"Questa è la temperatura del piano per i layer (tranne il primo). Un valore " +"pari a 0 indica che il filamento non supporta la stampa su piatto " +"Engineering." msgid "" "Bed temperature for layers except the initial one. Value 0 means the " "filament does not support to print on the High Temp Plate" msgstr "" -"This is the bed temperature for layers except for the first one. A value of " -"0 means the filament does not support printing on the High Temp Plate." +"Questa è la temperatura del piano per i layer (tranne il primo). Un valore " +"pari a 0 indica che il filamento non supporta la stampa sul High Temp." msgid "" "Bed temperature for layers except the initial one. Value 0 means the " @@ -7474,31 +7689,31 @@ msgstr "" "filamento non supportata la stampa su piatto Textured PEI." msgid "Initial layer" -msgstr "First layer" +msgstr "Primo layer" msgid "Initial layer bed temperature" -msgstr "First layer bed temperature" +msgstr "Temperatura del piano per il primo layer" msgid "" "Bed temperature of the initial layer. Value 0 means the filament does not " "support to print on the Cool Plate" msgstr "" -"This is the bed temperature of the first layer. A value of 0 means the " -"filament does not support printing on the Cool Plate." +"Questa è la temperatura del piatto del primo layer. Un valore pari a 0 " +"indica che il filamento non supporta la stampa sul piatto Cool Plate." msgid "" "Bed temperature of the initial layer. Value 0 means the filament does not " "support to print on the Engineering Plate" msgstr "" -"This is the bed temperature of the first layer. A value of 0 means the " -"filament does not support printing on the Engineering Plate." +"Temperatura del piano quando è installato il piatto Cool Plate. Il valore 0 " +"significa che il filamento non supporta la stampa su piatto Engineering." msgid "" "Bed temperature of the initial layer. Value 0 means the filament does not " "support to print on the High Temp Plate" msgstr "" -"This is the bed temperature of the first layer. A value of 0 means the " -"filament does not support printing on the High Temp Plate." +"Questa è la temperatura del piano del primo layer. Un valore pari a 0 indica " +"che il filamento non supporta la stampa sul piatto High Temp." msgid "" "Bed temperature of the initial layer. Value 0 means the filament does not " @@ -7508,7 +7723,7 @@ msgstr "" "supportato sul piatto Textured PEI." msgid "Bed types supported by the printer" -msgstr "Bed types supported by the printer" +msgstr "Tipi di piatti supportati dalla stampante" msgid "Cool Plate" msgstr "Cool Plate" @@ -7517,25 +7732,27 @@ msgid "Engineering Plate" msgstr "Engineering Plate" msgid "First layer print sequence" -msgstr "" +msgstr "Sequenza di stampa del primo strato" msgid "This G-code is inserted at every layer change before lifting z" -msgstr "This G-code is inserted at every layer change before lifting z." +msgstr "" +"Questo G-code viene inserito ad ogni cambio layer prima del sollevamento z." msgid "Bottom shell layers" -msgstr "Bottom shell layers" +msgstr "Layer inferiori guscio" msgid "" "This is the number of solid layers of bottom shell, including the bottom " "surface layer. When the thickness calculated by this value is thinner than " "bottom shell thickness, the bottom shell layers will be increased" msgstr "" -"This is the number of solid layers of bottom shell, including the bottom " -"surface layer. When the thickness calculated by this value is thinner than " -"bottom shell thickness, the bottom shell layers will be increased" +"È il numero di layers solidi del guscio inferiore, compreso Il layer " +"superficiale inferiore. Se lo spessore calcolato da questo valore è più " +"sottile dello spessore del guscio inferiore, i layers del guscio inferiore " +"verranno aumentati." msgid "Bottom shell thickness" -msgstr "Bottom shell thickness" +msgstr "Spessore del guscio inferiore" msgid "" "The number of bottom solid layers is increased when slicing if the thickness " @@ -7544,11 +7761,12 @@ msgid "" "is disabled and thickness of bottom shell is absolutely determained by " "bottom shell layers" msgstr "" -"The number of bottom solid layers is increased when slicing if the thickness " -"calculated by bottom shell layers is thinner than this value. This can avoid " -"having too thin a shell when layer height is small. 0 means that this " -"setting is disabled and the thickness of the bottom shell is determined " -"simply by the number of bottom shell layers." +"Il numero di layers solidi inferiori aumenta durante l'elaborazione se lo " +"spessore calcolato dei layers del guscio inferiore è più sottile di questo " +"valore. Questo può evitare di avere un guscio troppo sottile quando " +"l'altezza layer è ridotta. 0 significa che questa impostazione è " +"disabilitata e lo spessore del guscio inferiore è determinato semplicemente " +"dal numero di layers del guscio inferiore." msgid "Force cooling for overhang and bridge" msgstr "Force cooling for overhangs and bridges" @@ -7557,11 +7775,12 @@ msgid "" "Enable this option to optimize part cooling fan speed for overhang and " "bridge to get better cooling" msgstr "" -"Enable this option to optimize the part cooling fan speed for overhangs and " -"bridges to get better cooling" +"Abilita questa opzione per ottimizzare la velocità della ventola di " +"raffreddamento degli oggetti per sporgenze e ponti per ottenere un " +"raffreddamento migliore." msgid "Fan speed for overhang" -msgstr "Fan speed for overhangs" +msgstr "Velocità della ventola per le sporgenze" msgid "" "Force part cooling fan to be this speed when printing bridge or overhang " @@ -7573,7 +7792,7 @@ msgstr "" "bridges can achieve better quality for these parts." msgid "Cooling overhang threshold" -msgstr "Cooling overhang threshold" +msgstr "Soglia di sbalzo per il raffreddamento" #, c-format msgid "" @@ -7589,7 +7808,7 @@ msgstr "" "esterna, indipendentemente dal grado di sporgenza." msgid "Bridge infill direction" -msgstr "" +msgstr "Direzione di riempimento del ponte" msgid "" "Bridging angle override. If left to zero, the bridging angle will be " @@ -7601,55 +7820,64 @@ msgstr "" "verrà utilizzato per i Bridge esterni. Usa 180° per un angolo zero." msgid "Bridge density" -msgstr "" +msgstr "Densità del ponte" msgid "Density of external bridges. 100% means solid bridge. Default is 100%." msgstr "" +"Densità di ponti esterni. 100% significa solido ponte. L'impostazione " +"predefinita è al 100%." msgid "Bridge flow" -msgstr "Bridge flow" +msgstr "Flusso del Bridge" msgid "" "Decrease this value slightly(for example 0.9) to reduce the amount of " "material for bridge, to improve sag" msgstr "" -"Decrease this value slightly (for example 0.9) to reduce the amount of " -"material extruded for bridges to avoid sagging." +"Diminuire leggermente questo valore (ad esempio 0.9) per ridurre la quantità " +"di materiale per il ponte e migliorare l'abbassamento dello stesso" msgid "Top surface flow ratio" -msgstr "" +msgstr "Rapporto di portata superficiale superiore" msgid "" "This factor affects the amount of material for top solid infill. You can " "decrease it slightly to have smooth surface finish" msgstr "" +"Questo fattore influisce sulla quantità di materiale per il riempimento " +"solido superiore. Puoi diminuirlo leggermente per avere una finitura " +"superficiale liscia" msgid "Bottom surface flow ratio" -msgstr "" +msgstr "Rapporto di flusso superficiale del fondo" msgid "This factor affects the amount of material for bottom solid infill" msgstr "" +"Questo fattore influisce sulla quantità di materiale per il riempimento " +"solido inferiore" msgid "Precise wall(experimental)" -msgstr "" +msgstr "Parete precisa (sperimentale)" msgid "" "Improve shell precision by adjusting outer wall spacing. This also improves " "layer consistency." msgstr "" +"Migliora la precisione dei proiettili regolando la spaziatura delle pareti " +"esterne. Questo migliora anche la consistenza degli strati." msgid "Only one wall on top surfaces" -msgstr "Only one wall on top surfaces" +msgstr "Solo una parete sulle superfici superiori" msgid "" "Use only one wall on flat top surface, to give more space to the top infill " "pattern" msgstr "" -"Use only one wall on flat top surfaces, to give more space to the top infill " -"pattern" +"Usa solo una parete su superfici piane, per dare più spazio alla trama " +"riempimento superiore" msgid "One wall threshold" -msgstr "" +msgstr "Una soglia da parete" #, c-format, boost-format msgid "" @@ -7662,6 +7890,15 @@ msgid "" "on the next layer, like letters. Set this setting to 0 to remove these " "artifacts." msgstr "" +"Se una superficie superiore deve essere stampata ed è parzialmente coperta " +"da un altro strato, non verrà considerata in un livello superiore in cui la " +"sua larghezza è inferiore a questo valore. Questo può essere utile per non " +"lasciare che il \"un perimetro in cima\" si attivi su una superficie che " +"dovrebbe essere coperta solo da perimetri. Questo valore può essere un mm o " +"un % of della larghezza di estrusione del perimetro.\n" +"Attenzione: se abilitato, è possibile creare artefatti se si hanno alcune " +"caratteristiche sottili sul livello successivo, come le lettere. Impostare " +"questa impostazione su 0 per rimuovere questi artefatti." msgid "Only one wall on first layer" msgstr "Solo un perimetro sul primo layer" @@ -7670,32 +7907,39 @@ msgid "" "Use only one wall on first layer, to give more space to the bottom infill " "pattern" msgstr "" +"Utilizzare un solo muro sul primo strato, per dare più spazio al modello di " +"riempimento inferiore" msgid "Extra perimeters on overhangs" -msgstr "" +msgstr "Perimetri aggiuntivi sulle sporgenze (sperimentale)" msgid "" "Create additional perimeter paths over steep overhangs and areas where " "bridges cannot be anchored. " msgstr "" +"Creare percorsi perimetrali aggiuntivi su strapiombi ripidi e aree in cui i " +"ponti non possono essere ancorati. " msgid "Reverse on odd" -msgstr "" +msgstr "Retromarcia su dispari" msgid "Overhang reversal" -msgstr "" +msgstr "Inversione di sbalzo" msgid "" "Extrude perimeters that have a part over an overhang in the reverse " "direction on odd layers. This alternating pattern can drastically improve " "steep overhang." msgstr "" +"Estrudere i perimetri che hanno una parte su una sporgenza nella direzione " +"inversa su layer dispari. Questo schema alternato può migliorare " +"drasticamente lo strapiombo ripido." msgid "Reverse threshold" -msgstr "" +msgstr "Soglia inversa" msgid "Overhang reversal threshold" -msgstr "" +msgstr "Soglia di inversione a sbalzo" #, c-format, boost-format msgid "" @@ -7703,54 +7947,61 @@ msgid "" "useful. Can be a % of the perimeter width.\n" "Value 0 enables reversal on every odd layers regardless." msgstr "" +"Numero di mm di sbalzo necessario affinché l'inversione sia considerata " +"utile. Può essere un % o della larghezza del perimetro.\n" +"Il valore 0 abilita l'inversione su tutti i livelli dispari a prescindere." msgid "Classic mode" -msgstr "" +msgstr "Modalità classica" msgid "Enable this option to use classic mode" -msgstr "" +msgstr "Abilita questa opzione per utilizzare la modalità classica" msgid "Slow down for overhang" -msgstr "Slow down for overhangs" +msgstr "Rallenta in caso di sporgenze" msgid "Enable this option to slow printing down for different overhang degree" msgstr "" -"Enable this option to slow down when printing overhangs. The speeds for " -"different overhang percentages are set below." +"Abilita questa opzione per rallentare quando la stampa presenta sporgenze. " +"Le velocità per le diverse percentuali di sporgenza sono indicate di seguito." msgid "Slow down for curled perimeters" -msgstr "" +msgstr "Rallenta per perimetri arricciati" msgid "" "Enable this option to slow printing down in areas where potential curled " "perimeters may exist" msgstr "" +"Attivare questa opzione per rallentare la stampa nelle aree in cui possono " +"esistere potenziali perimetri arricciati" msgid "mm/s or %" -msgstr "" +msgstr "mm/s o %" msgid "External" -msgstr "" +msgstr "Esterno" msgid "Speed of bridge and completely overhang wall" -msgstr "This is the speed for bridges and 100% overhang walls." +msgstr "E' la velocità per i ponti e le pareti pendenti al 100%." msgid "mm/s" msgstr "mm/s" msgid "Internal" -msgstr "" +msgstr "Interno" msgid "" "Speed of internal bridge. If the value is expressed as a percentage, it will " "be calculated based on the bridge_speed. Default value is 150%." msgstr "" +"Velocità del ponte interno. Se il valore è espresso in percentuale, verrà " +"calcolato in base al bridge_speed. Il valore predefinito è 150%." msgid "Brim width" msgstr "Larghezza brim" msgid "Distance from model to the outermost brim line" -msgstr "This is the distance from the model to the outermost brim line." +msgstr "Questa è la distanza tra il modello e la linea del brim più esterno." msgid "Brim type" msgstr "Tipo di brim" @@ -7770,61 +8021,68 @@ msgid "" "A gap between innermost brim line and object can make brim be removed more " "easily" msgstr "" -"This creates a gap between the innermost brim line and the object and can " -"make the brim easier to remove." +"Questo crea un gap tra la linea interna del brim e l'oggetto per rendere il " +"brim più facile da rimuovere" msgid "Brim ears" -msgstr "" +msgstr "Orecchie a tesa" msgid "Only draw brim over the sharp edges of the model." -msgstr "" +msgstr "Disegna la tesa solo sugli spigoli vivi del modello." msgid "Brim ear max angle" -msgstr "" +msgstr "Angolo massimo dell'orecchio della tesa" msgid "" "Maximum angle to let a brim ear appear. \n" "If set to 0, no brim will be created. \n" "If set to ~180, brim will be created on everything but straight sections." msgstr "" +"Angolo massimo per far apparire un orecchio a tesa. \n" +"Se impostato su 0, non verrà creato alcun telo. \n" +"Se impostato su ~180, l'orlo verrà creato su tutto tranne che sulle sezioni " +"diritte." msgid "Brim ear detection radius" -msgstr "" +msgstr "Raggio di rilevamento dell'orecchio della tesa" msgid "" "The geometry will be decimated before dectecting sharp angles. This " "parameter indicates the minimum length of the deviation for the decimation.\n" "0 to deactivate" msgstr "" +"La geometria verrà decimata prima di rilevare gli angoli acuti. Questo " +"parametro indica la lunghezza minima dello scostamento per la decimazione.\n" +"0 per disattivare" msgid "Compatible machine" -msgstr "Compatible machine" +msgstr "Macchina compatibile" msgid "upward compatible machine" msgstr "macchina compatibile con versioni successive" msgid "Compatible machine condition" -msgstr "Compatible machine condition" +msgstr "Condizione della macchina compatibile" msgid "Compatible process profiles" -msgstr "Compatible process profiles" +msgstr "Profili di processo compatibili" msgid "Compatible process profiles condition" -msgstr "Compatible process profiles condition" +msgstr "Condizione dei profili di processo compatibili" msgid "Print sequence, layer by layer or object by object" msgstr "" -"This determines the print sequence, allowing you to print layer-by-layer or " -"object-by-object." +"Questo determina la sequenza di stampa, che consente di stampare layer per " +"layer o oggetto per oggetto." msgid "By layer" -msgstr "By layer" +msgstr "Per layer" msgid "By object" -msgstr "By object" +msgstr "Per oggetto" msgid "Slow printing down for better layer cooling" -msgstr "Slow printing down for better layer cooling" +msgstr "Rallenta stampa per un migliore raffreddamento layers" msgid "" "Enable this option to slow printing speed down to make the final layer time " @@ -7832,20 +8090,21 @@ msgid "" "that layer can be cooled for longer time. This can improve the cooling " "quality for needle and small details" msgstr "" -"Enable this option to slow printing speed down to ensure that the final " -"layer time is not shorter than the layer time threshold in \"Max fan speed " -"threshold\", so that the layer can be cooled for a longer time. This can " -"improve the quality for small details." +"Abilita questa opzione per rallentare la velocità di stampa in modo che il " +"tempo finale del layer non sia inferiore alla soglia di tempo nel valore " +"\"Soglia di velocità massima della ventola\", in modo che il layer possa " +"essere raffreddato più a lungo.\n" +"Ciò può migliorare la qualità per i piccoli dettagli" msgid "Normal printing" -msgstr "Normal printing" +msgstr "Stampa normale" msgid "" "The default acceleration of both normal printing and travel except initial " "layer" msgstr "" -"This is the default acceleration for both normal printing and travel after " -"the first layer." +"E' l'accelerazione predefinita sia per la stampa normale che per la corsa " +"dopo il primo layer." msgid "mm/s²" msgstr "mm/s²" @@ -7854,19 +8113,23 @@ msgid "Default filament profile" msgstr "Profilo filamento predefinito" msgid "Default filament profile when switch to this machine profile" -msgstr "Default filament profile when switching to this machine profile" +msgstr "" +"Profilo filamento predefinito quando si passa a questo profilo macchina" msgid "Default process profile" -msgstr "Default process profile" +msgstr "Profilo di processo predefinito" msgid "Default process profile when switch to this machine profile" -msgstr "Default process profile when switching to this machine profile" +msgstr "" +"Profilo di processo predefinito quando si passa a questo profilo macchina" msgid "Activate air filtration" -msgstr "" +msgstr "Attivare la filtrazione dell'aria" msgid "Activate for better air filtration. G-code command: M106 P3 S(0-255)" msgstr "" +"Attivare per una migliore filtrazione dell'aria. Comando G-code: M106 P3 " +"S(0-255)" msgid "Fan speed" msgstr "Velocità ventola" @@ -7875,19 +8138,21 @@ msgid "" "Speed of exhuast fan during printing.This speed will overwrite the speed in " "filament custom gcode" msgstr "" +"Velocità della ventola di scarico durante la stampa. Questa velocità " +"sovrascriverà la velocità nel gcode personalizzato del filamento" msgid "Speed of exhuast fan after printing completes" -msgstr "" +msgstr "Velocità della ventola di estrazione al termine della stampa" msgid "No cooling for the first" -msgstr "No cooling for the first" +msgstr "Nessun raffreddamento per il primo" msgid "" "Close all cooling fan for the first certain layers. Cooling fan of the first " "layer used to be closed to get better build plate adhesion" msgstr "" -"Turn off all cooling fans for the first few layers. This can be used to " -"improve bed adhesion." +"Spegnere tutte le ventole di raffreddamento per i primi layer. Questo può " +"servire a migliorare l'adesione del piatto." msgid "Don't support bridges" msgstr "Non supportare i bridge" @@ -7920,18 +8185,18 @@ msgid "" "bridges to be supported, and set it to a very large value if you don't want " "any bridges to be supported." msgstr "" -"This is the maximum length of bridges that don't need support. Set it to 0 " -"if you want all bridges to be supported, and set it to a very large value if " -"you don't want any bridges to be supported." +"Questa è la lunghezza massima dei ponti che non necessitano di supporto. " +"Impostalo su 0 se desideri che tutti i bridge siano supportati e impostalo " +"su un valore molto grande se non vuoi che nessun bridge sia supportato." msgid "End G-code" msgstr "G-code finale" msgid "End G-code when finish the whole printing" -msgstr "Add end G-Code when finishing the entire print." +msgstr "Aggiungi G-code quando si termina l'intera stampa." msgid "End G-code when finish the printing of this filament" -msgstr "Add end G-code when finishing the printing of this filament." +msgstr "Aggiungi G-code quando si termina la stampa di questo filamento." msgid "Ensure vertical shell thickness" msgstr "Garantisci spessore verticale del guscio" @@ -7945,10 +8210,11 @@ msgstr "" "inferiori)." msgid "Top surface pattern" -msgstr "Top surface pattern" +msgstr "Trama superfice superiore" msgid "Line pattern of top surface infill" -msgstr "This is the line pattern for top surface infill." +msgstr "" +"Questo è la Trama lineare per il riempimento della superficie superiore." msgid "Concentric" msgstr "Concentrico" @@ -7960,7 +8226,7 @@ msgid "Monotonic" msgstr "Monotonico" msgid "Monotonic line" -msgstr "Monotonic line" +msgstr "Linea monotonica" msgid "Aligned Rectilinear" msgstr "Rettilineo allineato" @@ -7975,35 +8241,41 @@ msgid "Octagram Spiral" msgstr "Spirale a Ottogramma" msgid "Bottom surface pattern" -msgstr "Bottom surface pattern" +msgstr "Trama superficie inferiore" msgid "Line pattern of bottom surface infill, not bridge infill" msgstr "" -"This is the line pattern of bottom surface infill, not including bridge " -"infill." +"Questo è la trama lineare del riempimento della superficie inferiore, " +"escluso il riempimento del ponte." msgid "Internal solid infill pattern" -msgstr "" +msgstr "Schema di riempimento solido interno" msgid "" "Line pattern of internal solid infill. if the detect nattow internal solid " "infill be enabled, the concentric pattern will be used for the small area." msgstr "" +"Modello di linea del riempimento solido interno. Se l'opzione Rileva " +"riempimento solido interno Nattow è abilitata, il motivo concentrico verrà " +"utilizzato per l'area piccola." msgid "" "Line width of outer wall. If expressed as a %, it will be computed over the " "nozzle diameter." msgstr "" +"Larghezza della linea della parete esterna. Se espresso come %, verrà " +"calcolato sul diametro dell'ugello." msgid "" "Speed of outer wall which is outermost and visible. It's used to be slower " "than inner wall speed to get better quality." msgstr "" -"This is the printing speed for the outer walls of parts. These are generally " -"printed slower than inner walls for higher quality." +"E' la velocità di stampa per le pareti esterne dei pezzi. Queste vengono " +"generalmente stampate più lentamente delle pareti interne per ottenere una " +"qualità superiore." msgid "Small perimeters" -msgstr "" +msgstr "Perimetri piccoli" msgid "" "This separate setting will affect the speed of perimeters having radius <= " @@ -8011,37 +8283,44 @@ msgid "" "example: 80%) it will be calculated on the outer wall speed setting above. " "Set to zero for auto." msgstr "" +"Questa impostazione separata influenzerà la velocità dei perimetri con " +"raggio <= small_perimeter_threshold (di solito fori). Se espresso in " +"percentuale (ad esempio: 80%) verrà calcolato sull'impostazione della " +"velocità della parete esterna di cui sopra. Impostare su zero per auto." msgid "Small perimeters threshold" -msgstr "" +msgstr "Soglia perimetrale ridotta" msgid "" "This sets the threshold for small perimeter length. Default threshold is 0mm" msgstr "" +"In questo modo viene impostata la soglia per la lunghezza del perimetro " +"ridotta. La soglia predefinita è 0 mm" msgid "Order of inner wall/outer wall/infil" -msgstr "Order of inner wall/outer wall/infill" +msgstr "Ordine di parete interna/esterna/riempimento" msgid "Print sequence of inner wall, outer wall and infill. " -msgstr "This is the print sequence of inner walls, outer walls, and infill." +msgstr "" +"È la sequenza di stampa di pareti interne, pareti esterne e dei riempimenti." msgid "inner/outer/infill" -msgstr "inner/outer/infill" +msgstr "interno/esterno/riempimento" msgid "outer/inner/infill" -msgstr "outer/inner/infill" +msgstr "esterno/interno/riempimento" msgid "infill/inner/outer" -msgstr "infill/inner/outer" +msgstr "riempimento/interno/esterno" msgid "infill/outer/inner" -msgstr "infill/outer/inner" +msgstr "riempimento/esterno/interno" msgid "inner-outer-inner/infill" msgstr "interno-esterno-interno/riempimento" msgid "Height to rod" -msgstr "Height to rod" +msgstr "Altezza asta" msgid "" "Distance of the nozzle tip to the lower rod. Used for collision avoidance in " @@ -8051,7 +8330,7 @@ msgstr "" "le collisioni nella stampa di oggetto per oggetto." msgid "Height to lid" -msgstr "Height to lid" +msgstr "Altezza dal coperchio" msgid "" "Distance of the nozzle tip to the lid. Used for collision avoidance in by-" @@ -8071,13 +8350,13 @@ msgid "Extruder Color" msgstr "Colore estrusore" msgid "Only used as a visual help on UI" -msgstr "Only used as a visual help on UI" +msgstr "Utilizzato solo come aiuto visivo per l'interfaccia utente" msgid "Extruder offset" msgstr "Offset estrusore" msgid "Flow ratio" -msgstr "Flow ratio" +msgstr "Rapporto di flusso" msgid "" "The material may have volumetric change after switching between molten state " @@ -8086,27 +8365,33 @@ msgid "" "and 1.05. Maybe you can tune this value to get nice flat surface when there " "has slight overflow or underflow" msgstr "" -"The material may have volumetric change after switching between molten and " -"crystalline states. This setting changes all extrusion flow of this filament " -"in G-code proportionally. The recommended value range is between 0.95 and " -"1.05. You may be able to tune this value to get a nice flat surface if there " -"is slight overflow or underflow." +"Il materiale può subire variazioni volumetriche dopo il passaggio dallo " +"stato fuso a quello cristallino. Questa impostazione modifica in modo " +"proporzionale tutti i flussi di estrusione di questo filamento in G-code. " +"L'intervallo di valori raccomandato è compreso tra 0,95 e 1,05. È possibile " +"regolare questo valore per ottenere una superficie piatta se si verifica una " +"leggera sovra-estrusione o sotto-estrusione." msgid "Enable pressure advance" -msgstr "" +msgstr "Abilita l'avanzamento della pressione" msgid "" "Enable pressure advance, auto calibration result will be overwriten once " "enabled." msgstr "" +"Abilita l'avanzamento della pressione, il risultato della calibrazione " +"automatica verrà sovrascritto una volta abilitato." msgid "Pressure advance(Klipper) AKA Linear advance factor(Marlin)" msgstr "" +"Anticipo di pressione (Klipper) AKA Fattore di avanzamento lineare (Marlin)" msgid "" "Default line width if other line widths are set to 0. If expressed as a %, " "it will be computed over the nozzle diameter." msgstr "" +"Larghezza di linea predefinita se le altre larghezze di linea sono impostate " +"su 0. Se espresso come %, verrà calcolato sul diametro dell'ugello." msgid "Keep fan always on" msgstr "Mantieni la ventola sempre accesa" @@ -8115,9 +8400,9 @@ msgid "" "If enable this setting, part cooling fan will never be stoped and will run " "at least at minimum speed to reduce the frequency of starting and stoping" msgstr "" -"Enabling this setting means that part cooling fan will never stop entirely " -"and will instead run at least at minimum speed to reduce the frequency of " -"starting and stopping." +"Se si attiva questa impostazione, la ventola di raffreddamento non si " +"arresterà mai del tutto, ma funzionerà almeno alla velocità minima per " +"ridurre la frequenza di avvio e arresto." msgid "Layer time" msgstr "Layer time" @@ -8127,9 +8412,10 @@ msgid "" "shorter than this value. Fan speed is interpolated between the minimum and " "maximum fan speeds according to layer printing time" msgstr "" -"The part cooling fan will be enabled for layers where the estimated time is " -"shorter than this value. Fan speed is interpolated between the minimum and " -"maximum fan speeds according to layer printing time." +"La ventola di raffreddamento parziale verrà attivata per i layers in cui il " +"tempo stimato è inferiore a questo valore. La velocità della ventola viene " +"interpolata tra la velocità minima e massima della ventola in base al tempo " +"di stampa a layer." msgid "Default color" msgstr "Colore predefinito" @@ -8141,10 +8427,10 @@ msgid "Color" msgstr "Colore" msgid "Filament notes" -msgstr "" +msgstr "Note filamento" msgid "You can put your notes regarding the filament here." -msgstr "" +msgstr "È possibile inserire qui le note riguardanti il filamento." msgid "Required nozzle HRC" msgstr "Necessita nozzle HRC" @@ -8174,26 +8460,26 @@ msgstr "Durata caricamento filamento" msgid "Time to load new filament when switch filament. For statistics only" msgstr "" -"Time to load new filament when switching filament, for statistical purposes " -"only." +"Tempo di caricamento del nuovo filamento quando si cambia filamento, solo a " +"fini statistici." msgid "Filament unload time" msgstr "Durata scaricamento filamento" msgid "Time to unload old filament when switch filament. For statistics only" msgstr "" -"Time to unload old filament when switching filament, for statistical " -"purposes only." +"Tempo di scarico vecchio filamento quando si cambia filamento, solo a fini " +"statistici." msgid "" "Filament diameter is used to calculate extrusion in gcode, so it's important " "and should be accurate" msgstr "" -"Filament diameter is used to calculate extrusion variables in G-code, so it " -"is important that this is accurate and precise." +"Il diametro del filamento viene utilizzato per calcolare le variabili di " +"estrusione nel G-code, quindi è importante che sia accurato e preciso." msgid "Shrinkage" -msgstr "" +msgstr "Restringimento" #, c-format, boost-format msgid "" @@ -8203,56 +8489,73 @@ msgid "" "Be sure to allow enough space between objects, as this compensation is done " "after the checks." msgstr "" +"Inserisci la percentuale di restringimento che il filamento otterrà dopo il " +"raffreddamento (94% if misuri 94 mm invece di 100 mm). La parte verrà " +"scalata in xy per compensare. Viene preso in considerazione solo il " +"filamento utilizzato per il perimetro.\n" +"Assicurarsi di lasciare uno spazio sufficiente tra gli oggetti, poiché " +"questa compensazione viene eseguita dopo i controlli." msgid "Loading speed" -msgstr "" +msgstr "Velocità di caricamento" msgid "Speed used for loading the filament on the wipe tower." -msgstr "" +msgstr "Velocità utilizzata per caricare il filamento sulla torre di pulitura." msgid "Loading speed at the start" -msgstr "" +msgstr "Velocità iniziale di caricamento" msgid "Speed used at the very beginning of loading phase." -msgstr "" +msgstr "Velocità utilizzata all'inizio della fase di caricamento." msgid "Unloading speed" -msgstr "" +msgstr "Velocità di scaricamento" msgid "" "Speed used for unloading the filament on the wipe tower (does not affect " "initial part of unloading just after ramming)." msgstr "" +"Velocità usata per scaricare il filamento sulla torre di pulitura (non " +"influisce sulla parte iniziale dello scaricamento dopo il ramming)." msgid "Unloading speed at the start" -msgstr "" +msgstr "Velocità iniziale di scaricamento" msgid "" "Speed used for unloading the tip of the filament immediately after ramming." msgstr "" +"Velocità utilizzata per scaricare la punta del filamento immediatamente dopo " +"il ramming." msgid "Delay after unloading" -msgstr "" +msgstr "Ritardo dopo lo scarico" msgid "" "Time to wait after the filament is unloaded. May help to get reliable " "toolchanges with flexible materials that may need more time to shrink to " "original dimensions." msgstr "" +"Tempo di attesa dopo lo scarico del filamento. Può aiutare ad ottenere cambi " +"affidabili con materiali flessibili che potrebbero richiedere più tempo per " +"tornare alle dimensioni originali." msgid "Number of cooling moves" -msgstr "" +msgstr "Numero di movimenti di raffreddamento" msgid "" "Filament is cooled by being moved back and forth in the cooling tubes. " "Specify desired number of these moves." msgstr "" +"Il filamento è raffreddato venendo spostato avanti e indietro nei tubi di " +"raffreddamento. Specificare il numero desiderato di questi movimenti." msgid "Speed of the first cooling move" -msgstr "" +msgstr "Velocità del primo movimento di raffreddamento" msgid "Cooling moves are gradually accelerating beginning at this speed." msgstr "" +"I movimenti di raffreddamento accelerano gradualmente partendo da questa " +"velocità." msgid "Minimal purge on wipe tower" msgstr "Spurgo minimo sulla torre di pulitura" @@ -8264,35 +8567,52 @@ msgid "" "object, Slic3r will always prime this amount of material into the wipe tower " "to produce successive infill or sacrificial object extrusions reliably." msgstr "" +"Dopo un cambio di strumento, l'esatta posizione del filamento appena " +"caricato dentro l'ugello potrebbe essere sconosciuta, e la pressione del " +"filamento probabilmente non è ancora stabile. Prima di spurgare la testina " +"di stampa nel riempimento o in un oggetto sacrificale, Slic3r posizionerà " +"questo materiale in una torre di pulitura al fine di ottenere una successiva " +"estrusione affidabile su oggetto sacrificale o riempimento." msgid "Speed of the last cooling move" -msgstr "" +msgstr "Velocità dell'ultimo movimento di raffreddamento" msgid "Cooling moves are gradually accelerating towards this speed." msgstr "" +"I movimenti di raffreddamento accelerano gradualmente verso questa velocità." msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " "filament during a tool change (when executing the T code). This time is " "added to the total print time by the G-code time estimator." msgstr "" +"Tempo per il firmware della stampante (o per l'unità Multi Material 2.0) per " +"il caricamento del nuovo filamento durante il cambio strumento (quando viene " +"eseguito il T code). Questa durata viene aggiunta alla stima del tempo " +"totale di stampa del G-code." msgid "Ramming parameters" -msgstr "" +msgstr "Parametri del ramming" msgid "" "This string is edited by RammingDialog and contains ramming specific " "parameters." msgstr "" +"Questa stringa viene controllata da RammingDialog e contiene parametri " +"specifici del ramming." msgid "" "Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " "filament during a tool change (when executing the T code). This time is " "added to the total print time by the G-code time estimator." msgstr "" +"Tempo per il firmware della stampante (o per l'unità Multi Material 2.0) per " +"lo scaricamento del nuovo filamento durante il cambio strumento (quando " +"viene eseguito il T code). Questa durata viene aggiunta alla stima del tempo " +"totale di stampa del G-code." msgid "Enable ramming for multitool setups" -msgstr "" +msgstr "Abilita ramming per configurazioni multitool" msgid "" "Perform ramming when using multitool printer (i.e. when the 'Single Extruder " @@ -8300,30 +8620,36 @@ msgid "" "amount of filament is rapidly extruded on the wipe tower just before the " "toolchange. This option is only used when the wipe tower is enabled." msgstr "" +"Esegue il ramming quando si usa una stampante multitool (Ad esempio, quando " +"l'opzione \"Multimateriale a estrusore singolo\" nelle impostazioni della " +"stampante è deselezionata.). Quando è selezionata, una piccola quantità di " +"filamento viene estrusa rapidamente sulla torre di pulitura appena prima del " +"cambio strumento. Questa opzione viene utilizzata solo quando la torre di " +"pulitura è abilitata." msgid "Multitool ramming volume" -msgstr "" +msgstr "Volume ramming multitool" msgid "The volume to be rammed before the toolchange." -msgstr "" +msgstr "Il volume di ramming prima del cambio strumento." msgid "Multitool ramming flow" -msgstr "" +msgstr "Flusso ramming multitool" msgid "Flow used for ramming the filament before the toolchange." -msgstr "" +msgstr "Flusso usato per il ramming del filamento prima del cambio strumento." msgid "Density" msgstr "Densità" msgid "Filament density. For statistics only" -msgstr "Filament density, for statistical purposes only." +msgstr "Densità filamento, solo a fini statistici." msgid "g/cm³" msgstr "g/cm³" msgid "The material type of filament" -msgstr "Filament material type" +msgstr "Tipo di filamento" msgid "Soluble material" msgstr "Materiale solubile" @@ -8331,7 +8657,8 @@ msgstr "Materiale solubile" msgid "" "Soluble material is commonly used to print support and support interface" msgstr "" -"Soluble material is commonly used to print support and support interfaces" +"Il materiale solubile viene comunemente utilizzato per stampare il supporto " +"e l'interfaccia di supporto" msgid "Support material" msgstr "Materiale di supporto" @@ -8339,59 +8666,64 @@ msgstr "Materiale di supporto" msgid "" "Support material is commonly used to print support and support interface" msgstr "" -"Support material is commonly used to print support and support interfaces." +"Il materiale di supporto viene comunemente utilizzato per stampare il " +"supporto e le interfacce di supporto." msgid "Softening temperature" -msgstr "" +msgstr "Temperatura di rammollimento" msgid "" "The material softens at this temperature, so when the bed temperature is " "equal to or greater than it, it's highly recommended to open the front door " "and/or remove the upper glass to avoid cloggings." msgstr "" +"Il materiale si ammorbidisce a questa temperatura, quindi quando la " +"temperatura del letto è uguale o superiore ad essa, si consiglia vivamente " +"di aprire la porta d'ingresso e/o rimuovere il vetro superiore per evitare " +"ostruzioni." msgid "Price" -msgstr "Price" +msgstr "Prezzo" msgid "Filament price. For statistics only" -msgstr "Filament price, for statistical purposes only." +msgstr "Prezzo del filamento, solo a fini statistici." msgid "money/kg" msgstr "soldi/kg" msgid "Vendor" -msgstr "" +msgstr "Venditore" msgid "Vendor of filament. For show only" -msgstr "" +msgstr "Venditore di filamenti. Solo per lo spettacolo" msgid "(Undefined)" -msgstr "(Undefined)" +msgstr "(Indefinito)" msgid "Infill direction" -msgstr "Infill direction" +msgstr "Direzione riempimento" msgid "" "Angle for sparse infill pattern, which controls the start or main direction " "of line" msgstr "" -"This is the angle for sparse infill pattern, which controls the start or " -"main direction of lines." +"Questo è l'angolo della trama di riempimento che controlla l'inizio o la " +"direzione principale delle linee." msgid "Sparse infill density" -msgstr "Sparse infill density" +msgstr "Densità riempimento" #, c-format msgid "Density of internal sparse infill, 100% means solid throughout" msgstr "" -"This is the density of internal sparse infill. 100% means that the object " -"will be solid throughout." +"Questa è la densità del riempimento interno. 100%% significa che l'oggetto " +"sarà in ogni sua parte." msgid "Sparse infill pattern" -msgstr "Sparse infill pattern" +msgstr "Trama riempimento" msgid "Line pattern for internal sparse infill" -msgstr "This is the line pattern for internal sparse infill." +msgstr "Questo è la trama lineare per il riempimento interno." msgid "Grid" msgstr "Griglia" @@ -8403,7 +8735,7 @@ msgid "Cubic" msgstr "Cubico" msgid "Tri-hexagon" -msgstr "Tri-hexagon" +msgstr "Tri-esagono" msgid "Gyroid" msgstr "Giroide" @@ -8424,7 +8756,7 @@ msgid "Lightning" msgstr "Lightning" msgid "Sparse infill anchor length" -msgstr "" +msgstr "Lunghezza dell'ancora di riempimento sparsa" msgid "" "Connect an infill line to an internal perimeter with a short segment of an " @@ -8437,6 +8769,16 @@ msgid "" "Set this parameter to zero to disable anchoring perimeters connected to a " "single infill line." msgstr "" +"Collegare una linea di riempimento a un perimetro interno con un breve " +"segmento di un perimetro aggiuntivo. Se espresso in percentuale (esempio: " +"15%) viene calcolato sulla larghezza di estrusione del riempimento. Slic3r " +"tenta di collegare due linee di riempimento ravvicinate a un breve segmento " +"perimetrale. Se non viene trovato alcun segmento perimetrale più corto di " +"infill_anchor_max, la linea di riempimento viene collegata a un segmento " +"perimetrale su un solo lato e la lunghezza del segmento perimetrale preso è " +"limitata a questo parametro, ma non più lunga di anchor_length_max. \n" +"Impostare questo parametro su zero per disabilitare i perimetri di " +"ancoraggio collegati a una singola linea di riempimento." msgid "0 (no open anchors)" msgstr "0 (senza ancore aperte)" @@ -8445,7 +8787,7 @@ msgid "1000 (unlimited)" msgstr "1000 (senza limiti)" msgid "Maximum length of the infill anchor" -msgstr "" +msgstr "Lunghezza massima dell'ancoraggio del riempimento" msgid "" "Connect an infill line to an internal perimeter with a short segment of an " @@ -8458,25 +8800,36 @@ msgid "" "If set to 0, the old algorithm for infill connection will be used, it should " "create the same result as with 1000 & 0." msgstr "" +"Collegare una linea di riempimento a un perimetro interno con un breve " +"segmento di un perimetro aggiuntivo. Se espresso in percentuale (esempio: " +"15%) viene calcolato sulla larghezza di estrusione del riempimento. Slic3r " +"tenta di collegare due linee di riempimento ravvicinate a un breve segmento " +"perimetrale. Se non viene trovato alcun segmento perimetrale più corto di " +"questo parametro, la linea di riempimento viene collegata a un segmento " +"perimetrale su un solo lato e la lunghezza del segmento perimetrale preso è " +"limitata a infill_anchor, ma non più lunga di questo parametro. \n" +"Se impostato a 0, verrà utilizzato il vecchio algoritmo per la connessione " +"di riempimento, che dovrebbe creare lo stesso risultato di 1000 e 0." msgid "0 (Simple connect)" -msgstr "" +msgstr "0 (Connessione semplice)" msgid "Acceleration of outer walls" -msgstr "" +msgstr "Accelerazione delle pareti esterne" msgid "Acceleration of inner walls" -msgstr "" +msgstr "Accelerazione delle pareti interne" msgid "Acceleration of travel moves" -msgstr "" +msgstr "Accelerazione massima per gli spostamenti" msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" msgstr "" -"This is the acceleration of top surface infill. Using a lower value may " -"improve top surface quality." +"Questa è l'accelerazione del riempimento della superficie superiore. " +"L'utilizzo di un valore inferiore può migliorare la qualità della superficie " +"superiore." msgid "Acceleration of outer wall. Using a lower value can improve quality" msgstr "" @@ -8487,122 +8840,143 @@ msgid "" "Acceleration of bridges. If the value is expressed as a percentage (e.g. " "50%), it will be calculated based on the outer wall acceleration." msgstr "" +"Accelerazione dei ponti. Se il valore è espresso in percentuale (ad es. " +"50%), verrà calcolato in base all'accelerazione della parete esterna." msgid "mm/s² or %" -msgstr "" +msgstr "mm/s o %" msgid "" "Acceleration of sparse infill. If the value is expressed as a percentage (e." "g. 100%), it will be calculated based on the default acceleration." msgstr "" +"Accelerazione del riempimento rado. Se il valore è espresso in percentuale " +"(ad esempio 100%), verrà calcolato in base all'accelerazione predefinita." msgid "" "Acceleration of internal solid infill. If the value is expressed as a " "percentage (e.g. 100%), it will be calculated based on the default " "acceleration." msgstr "" +"Accelerazione del riempimento solido interno. Se il valore è espresso in " +"percentuale (ad esempio 100%), verrà calcolato in base all'accelerazione " +"predefinita." msgid "" "Acceleration of initial layer. Using a lower value can improve build plate " "adhesive" msgstr "" -"This is the printing acceleration for the first layer. Using limited " -"acceleration can improve build plate adhesion." +"Questa è l'accelerazione di stampa per il primo layer. L'uso di \n" +"un'accelerazione limitata può migliorare l'adesione sul piatto" msgid "Enable accel_to_decel" -msgstr "" +msgstr "Abilita accel_to_decel" msgid "Klipper's max_accel_to_decel will be adjusted automatically" -msgstr "" +msgstr "La max_accel_to_decel di Klipper verrà regolata automaticamente" msgid "accel_to_decel" -msgstr "" +msgstr "accel_to_decel" #, c-format, boost-format msgid "" "Klipper's max_accel_to_decel will be adjusted to this %% of acceleration" msgstr "" +"La max_accel_to_decel di Klipper sarà adattata a questo %% di accelerazione" #, c-format, boost-format msgid "%%" -msgstr "" +msgstr "%%" msgid "Jerk of outer walls" -msgstr "" +msgstr "Strappo delle pareti esterne" msgid "Jerk of inner walls" -msgstr "" +msgstr "Sussulto delle pareti interne" msgid "Jerk for top surface" -msgstr "" +msgstr "Jerk per la superficie superiore" msgid "Jerk for infill" -msgstr "" +msgstr "Jerk per riempimento" msgid "Jerk for initial layer" -msgstr "" +msgstr "Jerk per lo strato iniziale" msgid "Jerk for travel" -msgstr "" +msgstr "Jerk per i viaggi" msgid "" "Line width of initial layer. If expressed as a %, it will be computed over " "the nozzle diameter." msgstr "" +"Larghezza della linea del livello iniziale. Se espresso come %, verrà " +"calcolato sul diametro dell'ugello." msgid "Initial layer height" -msgstr "Altezza layer iniziale" +msgstr "Altezza primo layer" msgid "" "Height of initial layer. Making initial layer height to be thick slightly " "can improve build plate adhension" msgstr "" -"This is the height of the first layer. Making the first layer height thicker " -"can improve build plate adhesion." +"Questa è l'altezza layer iniziale. L'aumento dell'altezza del primo layer " +"può migliorare l'adesione al piatto" msgid "Speed of initial layer except the solid infill part" msgstr "" -"This is the speed for the first layer except for solid infill sections." +"E' la velocità per il primo layer, tranne che per le sezioni di riempimento " +"solido." msgid "Initial layer infill" -msgstr "First layer infill" +msgstr "Riempimento primo layer" msgid "Speed of solid infill part of initial layer" -msgstr "This is the speed for solid infill parts of the first layer." +msgstr "E' la velocità per le parti di riempimento solido del primo layer." msgid "Initial layer travel speed" -msgstr "" +msgstr "Velocità di traslazione dello strato iniziale" msgid "Travel speed of initial layer" -msgstr "" +msgstr "Velocità di traslazione dello strato iniziale" msgid "Number of slow layers" -msgstr "" +msgstr "Numero di livelli lenti" msgid "" "The first few layers are printed slower than normal. The speed is gradually " "increased in a linear fashion over the specified number of layers." msgstr "" +"I primi strati vengono stampati più lentamente del normale. La velocità " +"viene gradualmente aumentata in modo lineare sul numero di strati " +"specificato." msgid "Initial layer nozzle temperature" -msgstr "First layer nozzle temperature" +msgstr "Temperatura nozzle primo layer" msgid "Nozzle temperature to print initial layer when using this filament" -msgstr "Nozzle temperature for printing the first layer with this filament" +msgstr "" +"Temperatura del nozzle per la stampa del primo layer con questo filamento" msgid "Full fan speed at layer" msgstr "Massima velocità della ventola al layer" msgid "" "Fan speed will be ramped up linearly from zero at layer " -"\"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer" -"\". \"full_fan_speed_layer\" will be ignored if lower than " -"\"close_fan_the_first_x_layers\", in which case the fan will be running at " -"maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." +"\"close_fan_the_first_x_layers\" to maximum at layer " +"\"full_fan_speed_layer\". \"full_fan_speed_layer\" will be ignored if lower " +"than \"close_fan_the_first_x_layers\", in which case the fan will be running " +"at maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." msgstr "" +"La velocità della ventola aumenterà linearmente da zero al livello " +"\"close_fan_the_first_x_layers\" al massimo al livello " +"\"full_fan_speed_layer\". \"full_fan_speed_layer\" verrà ignorato se " +"inferiore a \"close_fan_the_first_x_layers\", nel qual caso la ventola " +"funzionerà alla massima velocità consentita al livello " +"\"close_fan_the_first_x_layers\" + 1." msgid "Support interface fan speed" -msgstr "" +msgstr "Supporta la velocità della ventola dell'interfaccia" msgid "" "This fan speed is enforced during all support interfaces, to be able to " @@ -8610,14 +8984,19 @@ msgid "" "Set to -1 to disable this override.\n" "Can only be overriden by disable_fan_first_layers." msgstr "" +"Questa velocità della ventola viene applicata durante tutte le interfacce di " +"supporto, per essere in grado di indebolire il loro legame con un'elevata " +"velocità della ventola.\n" +"Impostare su -1 per disabilitare questa sostituzione.\n" +"Può essere sovrascritto solo da disable_fan_first_layers." msgid "" "Randomly jitter while printing the wall, so that the surface has a rough " "look. This setting controls the fuzzy position" msgstr "" -"This setting makes the toolhead randomly jitter while printing walls so that " -"the surface has a rough textured look. This setting controls the fuzzy " -"position." +"Questa impostazione fa vibrare casualmente la testa di stampa durante la " +"stampa su pareti, in modo che la superficie abbia un aspetto ruvido. Questa " +"impostazione controlla la posizione fuzzy Skin." msgid "None" msgstr "Nessuno" @@ -8638,8 +9017,8 @@ msgid "" "The width within which to jitter. It's adversed to be below outer wall line " "width" msgstr "" -"The width of jittering: it’s recommended to keep this lower than the outer " -"wall line width." +"Ampiezza del tremolio: si consiglia di mantenerla inferiore alla larghezza " +"della linea della parete esterna." msgid "Fuzzy skin point distance" msgstr "Distanza punti superficie crespa" @@ -8648,24 +9027,23 @@ msgid "" "The average diatance between the random points introducded on each line " "segment" msgstr "" -"The average distance between the random points introduced on each line " -"segment" +"La distanza media tra i punti casuali introdotti su ogni segmento di linea" msgid "Filter out tiny gaps" -msgstr "" +msgstr "Filtra i piccoli spazi vuoti" msgid "Layers and Perimeters" msgstr "Layers e Perimetri" msgid "Filter out gaps smaller than the threshold specified" -msgstr "" +msgstr "Filtra gli spazi più piccoli della soglia specificata" msgid "" "Speed of gap infill. Gap usually has irregular line width and should be " "printed more slowly" msgstr "" -"This is the speed for gap infill. Gaps usually have irregular line width and " -"should be printed more slowly." +"E' la velocità per il riempimento del gap. I gap hanno solitamente una " +"larghezza linea irregolare e devono essere stampate più lentamente." msgid "Arc fitting" msgstr "Arc fitting" @@ -8674,47 +9052,49 @@ msgid "" "Enable this to get a G-code file which has G2 and G3 moves. And the fitting " "tolerance is same with resolution" msgstr "" -"Enable this to get a G-code file with G2 and G3 moves. The fitting tolerance " -"is the same as the resolution." +"Abilita questa funzione per ottenere un file G-code con gli spostamenti " +"circolari in G2 e G3. La tolleranza di adattamento è la stessa della " +"risoluzione" msgid "Add line number" -msgstr "Add line number" +msgstr "Aggiungi numero di riga" msgid "Enable this to add line number(Nx) at the beginning of each G-Code line" msgstr "" -"Enable this to add line number(Nx) at the beginning of each G-Code line." +"Abilita questa opzione per aggiungere il numero di riga (Nx) all'inizio di " +"ogni riga del G-code" msgid "Scan first layer" -msgstr "Scan first layer" +msgstr "Scansiona Primo layer" msgid "" "Enable this to enable the camera on printer to check the quality of first " "layer" msgstr "" -"Enable this to allow the camera on the printer to check the quality of the " -"first layer." +"Attivare questa opzione per consentire alla fotocamera della stampante di " +"verificare la qualità del primo layer." msgid "Nozzle type" -msgstr "Nozzle type" +msgstr "Tipo di nozzle" msgid "" "The metallic material of nozzle. This determines the abrasive resistance of " "nozzle, and what kind of filament can be printed" msgstr "" -"The metallic material of the nozzle: This determines the abrasive resistance " -"of the nozzle and what kind of filament can be printed." +"Il materiale metallico del nozzle: determina la resistenza all'abrasione del " +"nozzle e il tipo di filamento che può essere stampato." msgid "Undefine" msgstr "Indefinito" msgid "Hardened steel" -msgstr "Hardened steel" +msgstr "Acciaio temprato" msgid "Stainless steel" -msgstr "Stainless steel" +msgstr "Acciaio inox" msgid "Brass" -msgstr "Brass" +msgstr "Ottone" msgid "Nozzle HRC" msgstr "Nozzle HRC" @@ -8730,33 +9110,37 @@ msgid "HRC" msgstr "HRC" msgid "Printer structure" -msgstr "" +msgstr "Struttura della stampante" msgid "The physical arrangement and components of a printing device" -msgstr "" +msgstr "La disposizione fisica e i componenti di un dispositivo di stampa" msgid "CoreXY" -msgstr "" +msgstr "CoreXY" msgid "I3" -msgstr "" +msgstr "I3" msgid "Hbot" -msgstr "" +msgstr "Hbot" msgid "Delta" -msgstr "" +msgstr "Delta" msgid "Best object position" -msgstr "" +msgstr "Migliore posizione dell'oggetto" msgid "Best auto arranging position in range [0,1] w.r.t. bed shape." msgstr "" +"Migliore posizione di disposizione automatica nell'intervallo [0,1] w.r.t. " +"forma del letto." msgid "" "Enable this option if machine has auxiliary part cooling fan. G-code " "command: M106 P2 S(0-255)." msgstr "" +"Abilitare questa opzione se la macchina è dotata di ventola di " +"raffreddamento della parte ausiliaria. Comando G-code: M106 P2 S(0-255)." msgid "" "Start the fan this number of seconds earlier than its target start time (you " @@ -8769,15 +9153,24 @@ msgid "" "gcode' is activated.\n" "Use 0 to deactivate." msgstr "" +"Avviare la ventola questo numero di secondi prima dell'ora di inizio " +"prevista (è possibile utilizzare i secondi frazionari). Si presume " +"un'accelerazione infinita per questa stima del tempo e si terrà conto solo " +"dei movimenti G1 e G0 (l'adattamento dell'arco non è supportato).\n" +"Non sposterà i comandi dei fan dai gcode personalizzati (agiscono come una " +"sorta di \"barriera\").\n" +"Non sposterà i comandi delle ventole nel gcode di avvio se è attivato " +"l'opzione \"solo gcode di avvio personalizzato\".\n" +"Utilizzare 0 per disattivare." msgid "Only overhangs" -msgstr "" +msgstr "Solo sporgenze" msgid "Will only take into account the delay for the cooling of overhangs." -msgstr "" +msgstr "Terrà conto solo del ritardo per il raffreddamento delle sporgenze." msgid "Fan kick-start time" -msgstr "" +msgstr "Tempo di avvio della ventola" msgid "" "Emit a max fan speed command for this amount of seconds before reducing to " @@ -8786,43 +9179,55 @@ msgid "" "fan started spinning from a stop, or to get the fan up to speed faster.\n" "Set to 0 to deactivate." msgstr "" +"Emettere un comando di velocità massima della ventola per questo numero di " +"secondi prima di ridurre la velocità target per avviare la ventola di " +"raffreddamento.\n" +"Ciò è utile per le ventole in cui un PWM/potenza bassa potrebbe essere " +"insufficiente per far partire la ventola da fermo o per far sì che la " +"ventola raggiunga la velocità più velocemente.\n" +"Impostare su 0 per disattivare." msgid "Time cost" -msgstr "" +msgstr "Costo in termini di tempo" msgid "The printer cost per hour" -msgstr "" +msgstr "Il costo orario della stampante" msgid "money/h" -msgstr "" +msgstr "soldi/h" msgid "Support control chamber temperature" -msgstr "" +msgstr "Supporta la temperatura della camera di controllo" msgid "" "This option is enabled if machine support controlling chamber temperature\n" "G-code command: M141 S(0-255)" msgstr "" +"Questa opzione è abilitata se la macchina supporta il controllo della " +"temperatura della camera\n" +"Comando G-code: M141 S(0-255)" msgid "Support air filtration" -msgstr "" +msgstr "Supporta la filtrazione dell'aria" msgid "" "Enable this if printer support air filtration\n" "G-code command: M106 P3 S(0-255)" msgstr "" +"Abilitare questa opzione se la stampante supporta il filtraggio dell'aria\n" +"Comando G-code: M106 P3 S(0-255)" msgid "G-code flavor" msgstr "Formato G-code" msgid "What kind of gcode the printer is compatible with" -msgstr "What kind of G-code the printer is compatible with." +msgstr "Con che tipo di G-code è compatibile la stampante." msgid "Klipper" -msgstr "" +msgstr "Klipper" msgid "Label objects" -msgstr "" +msgstr "Etichetta oggetti" msgid "" "Enable this to add comments into the G-Code labeling print moves with what " @@ -8830,24 +9235,32 @@ msgid "" "plugin. This settings is NOT compatible with Single Extruder Multi Material " "setup and Wipe into Object / Wipe into Infill." msgstr "" +"Attivalo per aggiungere commenti nel G-Code etichettando i movimenti di " +"stampa secondo l'appartenenza, utile per il plugin Octoprint CancelObject. " +"Questa impostazione NON è compatibile con una configurazione Multi Material " +"ad estrusore singolo e con Pulitura nell'oggetto / Pulitura nel riempimento." msgid "Exclude objects" -msgstr "" +msgstr "Escludi oggetti" msgid "Enable this option to add EXCLUDE OBJECT command in g-code" msgstr "" +"Abilita questa opzione per aggiungere il comando EXCLUDE OBJECT nel g-code" msgid "Verbose G-code" -msgstr "" +msgstr "G-code verboso" msgid "" "Enable this to get a commented G-code file, with each line explained by a " "descriptive text. If you print from SD card, the additional weight of the " "file could make your firmware slow down." msgstr "" +"Abilita per ottenere un file G-code commentato, con un testo descrittivo per " +"ciascuna linea. Se stampi da memoria SD, il peso aggiuntivo del file " +"potrebbe rallentare il firmware." msgid "Infill combination" -msgstr "Infill combination" +msgstr "Combinazione riempimento" msgid "" "Automatically Combine sparse infill of several layers to print together to " @@ -8857,35 +9270,40 @@ msgstr "" "order to reduce time. Walls are still printed with original layer height." msgid "Filament to print internal sparse infill." -msgstr "This is the filament for printing internal sparse infill." +msgstr "Questo è il filamento per la stampa del riempimento interno." msgid "" "Line width of internal sparse infill. If expressed as a %, it will be " "computed over the nozzle diameter." msgstr "" +"Larghezza della linea del riempimento interno sparso. Se espresso come %, " +"verrà calcolato sul diametro dell'ugello." msgid "Infill/Wall overlap" -msgstr "Infill/wall overlap" +msgstr "Sovrapposizione riempimento/parete" msgid "" "Infill area is enlarged slightly to overlap with wall for better bonding. " "The percentage value is relative to line width of sparse infill" msgstr "" -"This allows the infill area to be enlarged slightly to overlap with walls " -"for better bonding. The percentage value is relative to line width of sparse " -"infill." +"Ciò consente di allargare leggermente l'area di riempimento per sovrapporla " +"alle pareti per una migliore adesione. Il valore percentuale è relativo alla " +"larghezza della linea del riempimento." msgid "Speed of internal sparse infill" -msgstr "This is the speed for internal sparse infill." +msgstr "E' la velocità del riempimento interno." msgid "Interface shells" -msgstr "" +msgstr "Pareti interfaccia" msgid "" "Force the generation of solid shells between adjacent materials/volumes. " "Useful for multi-extruder prints with translucent materials or manual " "soluble support material" msgstr "" +"Forza la generazione di perimetri solidi tra volumi o materiali adiacenti. " +"Utile per stampe multi estrusore con materiali traslucidi o supporti " +"solubili manuali" msgid "Ironing Type" msgstr "Tipo di stiratura" @@ -8894,70 +9312,74 @@ msgid "" "Ironing is using small flow to print on same height of surface again to make " "flat surface more smooth. This setting controls which layer being ironed" msgstr "" -"Ironing uses a small flow to print at the same height of a surface to make " -"flat surfaces smoother. This setting controls which layers are being ironed." +"La stiratura utilizza un flusso ridotto per stampare alla stessa altezza di " +"una superficie per rendere le superfici piane più lisce. Questa impostazione " +"controlla quali layer vengono stirati." msgid "No ironing" -msgstr "No ironing" +msgstr "Non stirare" msgid "Top surfaces" -msgstr "All top surfaces" +msgstr "Tutte le superfici superiori" msgid "Topmost surface" -msgstr "Topmost surface only" +msgstr "Superficie superiore più alta" msgid "All solid layer" -msgstr "All solid layers" +msgstr "Tutti i layers solidi" msgid "Ironing Pattern" msgstr "Trama stiratura" msgid "The pattern that will be used when ironing" -msgstr "" +msgstr "Il modello che verrà utilizzato durante la stiratura" msgid "Ironing flow" -msgstr "Ironing flow" +msgstr "Flusso stiratura" msgid "" "The amount of material to extrude during ironing. Relative to flow of normal " "layer height. Too high value results in overextrusion on the surface" msgstr "" -"This is the amount of material to be extruded during ironing. It is relative " -"to the flow of normal layer height. Too high a value will result in " -"overextrusion on the surface." +"È la quantità di materiale da estrudere durante la stiratura. È relativo al " +"flusso dell'altezza normale del layer. Un valore troppo alto provoca una " +"sovraestrusione sulla superficie." msgid "Ironing line spacing" -msgstr "Ironing line spacing" +msgstr "Spaziatura linee di stiratura" msgid "The distance between the lines of ironing" -msgstr "This is the distance between the lines used for ironing." +msgstr "È la distanza tra le linee utilizzate per la stiratura." msgid "Ironing speed" -msgstr "Ironing speed" +msgstr "Velocità stiratura" msgid "Print speed of ironing lines" -msgstr "This is the print speed for ironing lines." +msgstr "È la velocità di stampa per le linee di stiratura." msgid "Ironing angle" -msgstr "" +msgstr "Angolo di stiratura" msgid "" "The angle ironing is done at. A negative number disables this function and " "uses the default method." msgstr "" +"La stiratura angolare viene eseguita a. Un numero negativo disabilita questa " +"funzione e utilizza il metodo predefinito." msgid "This gcode part is inserted at every layer change after lift z" -msgstr "This G-code is inserted at every layer change after the z lift." +msgstr "" +"Questo G-code viene inserito a ogni cambio di layer dopo l'elevazione z." msgid "Supports silent mode" -msgstr "Silent Mode" +msgstr "Modalità silenziosa" msgid "" "Whether the machine supports silent mode in which machine use lower " "acceleration to print" msgstr "" -"Whether the machine supports silent mode in which machine uses lower " -"acceleration to print more quietly" +"Se la macchina supporta la modalità silenziosa, in cui la macchina utilizza " +"un'accelerazione inferiore per stampare in modo più silenzioso" msgid "" "This G-code will be used as a code for the pause print. User can insert " @@ -8970,31 +9392,31 @@ msgid "This G-code will be used as a custom code" msgstr "Questo G-code verrà utilizzato come codice personalizzato" msgid "Maximum speed X" -msgstr "Maximum speed X" +msgstr "Velocità massima X" msgid "Maximum speed Y" -msgstr "Maximum speed Y" +msgstr "Velocità massima Y" msgid "Maximum speed Z" -msgstr "Maximum speed Z" +msgstr "Velocità massima Z" msgid "Maximum speed E" -msgstr "Maximum speed E" +msgstr "Velocità massima E" msgid "Machine limits" msgstr "Limiti macchina" msgid "Maximum X speed" -msgstr "Maximum X speed" +msgstr "Velocità massima X" msgid "Maximum Y speed" -msgstr "Maximum Y speed" +msgstr "Velocità massima Y" msgid "Maximum Z speed" -msgstr "Maximum Z speed" +msgstr "Velocità massima Z" msgid "Maximum E speed" -msgstr "Maximum E speed" +msgstr "Velocità massima E" msgid "Maximum acceleration X" msgstr "Accelerazione massima X" @@ -9045,41 +9467,43 @@ msgid "Maximum jerk of the E axis" msgstr "Jerk massimo dell'asse E" msgid "Minimum speed for extruding" -msgstr "Minimum speed for extruding" +msgstr "Velocità minima di estrusione" msgid "Minimum speed for extruding (M205 S)" -msgstr "Minimum speed for extruding (M205 S)" +msgstr "Velocità minima di estrusione (M205 S)" msgid "Minimum travel speed" -msgstr "Minimum travel speed" +msgstr "Velocità di spostamento minima" msgid "Minimum travel speed (M205 T)" -msgstr "Minimum travel speed (M205 T)" +msgstr "Velocità di spostamento minima (M205 T)" msgid "Maximum acceleration for extruding" -msgstr "Maximum acceleration for extruding" +msgstr "Accelerazione massima per l'estrusione" msgid "Maximum acceleration for extruding (M204 P)" -msgstr "Maximum acceleration for extruding (M204 P)" +msgstr "Accelerazione massima per l'estrusione (M204 P)" msgid "Maximum acceleration for retracting" -msgstr "Maximum acceleration for retracting" +msgstr "Accelerazione massima per la retrazione" msgid "Maximum acceleration for retracting (M204 R)" -msgstr "Maximum acceleration for retracting (M204 R)" +msgstr "Accelerazione massima per retrazione (M204 R)" msgid "Maximum acceleration for travel" -msgstr "Maximum acceleration for travel" +msgstr "Accelerazione massima per spostamenti" msgid "Maximum acceleration for travel (M204 T), it only applies to Marlin 2" msgstr "" +"Accelerazione massima per la corsa (M204 T), si applica solo al Marlin 2" msgid "" "Part cooling fan speed may be increased when auto cooling is enabled. This " "is the maximum speed limitation of part cooling fan" msgstr "" -"The part cooling fan speed may be increased when auto cooling is enabled. " -"This is the maximum speed for the part cooling fan." +"La velocità della ventola di raffreddamento può essere aumentata quando è " +"abilitato il raffreddamento automatico. Questa è la limitazione massima " +"della velocità della ventola di raffreddamento parziale" msgid "Max" msgstr "Massimo" @@ -9088,11 +9512,11 @@ msgid "" "The largest printable layer height for extruder. Used tp limits the maximum " "layer hight when enable adaptive layer height" msgstr "" -"The highest printable layer height for the extruder: this is used to limit " -"the maximum layer height when adaptive layer height is enabled." +"L'altezza massima del layer stampabile per l'estrusore: viene utilizzata per " +"limitare l'altezza massima del layer quando è abilitato il layer adattativo." msgid "Extrusion rate smoothing" -msgstr "" +msgstr "Levigatura della velocità di estrusione" msgid "" "This parameter smooths out sudden extrusion rate changes that happen when " @@ -9122,12 +9546,42 @@ msgid "" "\n" "Note: this parameter disables arc fitting." msgstr "" +"Questo parametro attenua le variazioni improvvise della velocità di " +"estrusione che si verificano quando la stampante passa dalla stampa di " +"un'estrusione ad alto flusso (alta velocità/larghezza maggiore) a " +"un'estrusione a flusso inferiore (velocità inferiore/larghezza inferiore) e " +"viceversa.\n" +"\n" +"Definisce la velocità massima con cui la portata volumetrica estrusa in mm3/" +"sec può variare nel tempo. Valori più alti significano che sono consentite " +"variazioni più elevate della velocità di estrusione, con conseguenti " +"transizioni di velocità più rapide.\n" +"\n" +"Il valore 0 disabilita la funzionalità. \n" +"\n" +"Per una stampante ad azionamento diretto ad alta velocità e ad alto flusso " +"(come Bambu lab o Voron) questo valore di solito non è necessario. Tuttavia, " +"può fornire alcuni vantaggi marginali in alcuni casi in cui le velocità " +"delle funzionalità variano notevolmente. Ad esempio, quando ci sono " +"rallentamenti aggressivi dovuti a strapiombi. In questi casi si consiglia un " +"valore elevato di circa 300-350 mm3/s2 in quanto ciò consente una levigatura " +"sufficiente per aiutare l'avanzamento della pressione a ottenere una " +"transizione di flusso più fluida.\n" +"\n" +"Per le stampanti più lente senza anticipo di pressione, il valore deve " +"essere impostato su un valore molto più basso. Un valore di 10-15 mm3/s2 è " +"un buon punto di partenza per gli estrusori a trasmissione diretta e di 5-10 " +"mm3/s2 per lo stile Bowden. \n" +"\n" +"Questa funzione è nota come equalizzatore di pressione in Prusa slicer.\n" +"\n" +"Nota: questo parametro disabilita l'adattamento dell'arco." msgid "mm³/s²" -msgstr "" +msgstr "mm³/s²" msgid "Smoothing segment length" -msgstr "" +msgstr "Levigatura della lunghezza del segmento" msgid "" "A lower value results in smoother extrusion rate transitions. However, this " @@ -9139,9 +9593,19 @@ msgid "" "\n" "Allowed values: 1-5" msgstr "" +"Un valore più basso si traduce in transizioni più fluide della velocità di " +"estrusione. Tuttavia, ciò si traduce in un file gcode significativamente più " +"grande e in un maggior numero di istruzioni per l'elaborazione da parte " +"della stampante. \n" +"\n" +"Il valore predefinito 3 funziona bene per la maggior parte dei casi. Se la " +"stampante balbetta, aumentare questo valore per ridurre il numero di " +"regolazioni effettuate\n" +"\n" +"Valori consentiti: 1-5" msgid "Minimum speed for part cooling fan" -msgstr "Minimum speed for part cooling fan" +msgstr "Velocità minima ventola di raffreddamento" msgid "" "Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed " @@ -9150,6 +9614,11 @@ msgid "" "Please enable auxiliary_fan in printer settings to use this feature. G-code " "command: M106 P2 S(0-255)" msgstr "" +"Velocità della ventola di raffreddamento della parte ausiliaria. La ventola " +"ausiliaria funzionerà a questa velocità durante la stampa, ad eccezione dei " +"primi strati, che sono definiti dall'assenza di strati di raffreddamento.\n" +"Abilitare auxiliary_fan nelle impostazioni della stampante per utilizzare " +"questa funzione. Comando G-code: M106 P2 S(0-255)" msgid "Min" msgstr "Minimo" @@ -9158,8 +9627,8 @@ msgid "" "The lowest printable layer height for extruder. Used tp limits the minimum " "layer hight when enable adaptive layer height" msgstr "" -"The lowest printable layer height for the extruder. This is used to limit " -"the minimum layer height when adaptive layer height is enabled." +"L'altezza minima del layer stampabile per l'estrusore. Viene utilizzata per " +"limitare l'altezza minima del layer quando è abilito il layer adattativo." msgid "Min print speed" msgstr "Velocità minima di stampa" @@ -9169,20 +9638,26 @@ msgid "" "cooling is enabled, when printing overhangs and when feature speeds are not " "specified explicitly." msgstr "" +"La velocità di stampa minima per il filamento quando è abilitato il " +"rallentamento per un migliore raffreddamento dello strato, quando le " +"sporgenze di stampa e quando le velocità delle caratteristiche non sono " +"specificate esplicitamente." msgid "Nozzle diameter" -msgstr "Diametro ugello" +msgstr "Diametro Nozzle" msgid "Diameter of nozzle" -msgstr "The diameter of the nozzle" +msgstr "Diametro del nozzle" msgid "Configuration notes" -msgstr "" +msgstr "Note di configurazione" msgid "" "You can put here your personal notes. This text will be added to the G-code " "header comments." msgstr "" +"È possibile inserire qui le note personali. Questo testo verrà aggiunto nei " +"commenti iniziali del G-code." msgid "Host Type" msgstr "Tipo host" @@ -9195,42 +9670,52 @@ msgstr "" "contenere il tipo di host." msgid "Nozzle volume" -msgstr "Nozzle volume" +msgstr "Volume del nozzle" msgid "Volume of nozzle between the cutter and the end of nozzle" -msgstr "Volume of nozzle between the filament cutter and the end of the nozzle" +msgstr "Volume del nozzle tra taglierina ed estremità nozzle" msgid "Cooling tube position" -msgstr "" +msgstr "Posizione tubo di raffreddamento" msgid "Distance of the center-point of the cooling tube from the extruder tip." msgstr "" +"Distanza del centro del tubo di raffreddamento dalla punta dell'estrusore." msgid "Cooling tube length" -msgstr "" +msgstr "Lunghezza del tubo di raffreddamento" msgid "Length of the cooling tube to limit space for cooling moves inside it." msgstr "" +"Lunghezza del tubo di raffreddamento per limitare lo spazio per i movimenti " +"di raffreddamento al suo interno." msgid "High extruder current on filament swap" -msgstr "" +msgstr "Alta corrente estrusore al cambio filamento" msgid "" "It may be beneficial to increase the extruder motor current during the " "filament exchange sequence to allow for rapid ramming feed rates and to " "overcome resistance when loading a filament with an ugly shaped tip." msgstr "" +"Potrebbe essere utile aumentare la corrente del motore estrusore durante la " +"sequenza di cambio filamento per permettere un avanzamento rapido del " +"ramming e per superare la resistenza durante il caricamento di un filamento " +"con una punta deformata." msgid "Filament parking position" -msgstr "" +msgstr "Posizione di parcheggio del filamento" msgid "" "Distance of the extruder tip from the position where the filament is parked " "when unloaded. This should match the value in printer firmware." msgstr "" +"Distanza della punta dell'estrusore dalla posizione dove il filamento viene " +"posto mentre viene scaricato. Dovrebbe essere uguale al valore nel firmware " +"della stampante." msgid "Extra loading distance" -msgstr "" +msgstr "Distanza di caricamento aggiuntiva" msgid "" "When set to zero, the distance the filament is moved from parking position " @@ -9238,6 +9723,11 @@ msgid "" "positive, it is loaded further, if negative, the loading move is shorter " "than unloading." msgstr "" +"Quando impostato a zero, la distanza percorsa dal filamento in posizione di " +"parcheggio durante il caricamento è esattamente uguale a quella contraria " +"durante lo scaricamento. Quando il valore è positivo, viene caricato " +"maggiormente, se il valore è negativo il movimento di caricamento è più " +"corto dello scaricamento." msgid "Start end points" msgstr "Punti iniziali e finali" @@ -9248,7 +9738,7 @@ msgstr "" "scarico." msgid "Reduce infill retraction" -msgstr "Reduce infill retraction" +msgstr "Riduci la retrazione nel riempimento" msgid "" "Don't retract when the travel is in infill area absolutely. That means the " @@ -9264,58 +9754,69 @@ msgid "Enable" msgstr "Abilita" msgid "Filename format" -msgstr "Filename format" +msgstr "Formato nome file" msgid "User can self-define the project file name when export" -msgstr "Users can decide project file names when exporting." +msgstr "" +"Gli utenti possono decidere i nomi dei file progetto nell'esportazione." msgid "Make overhang printable" -msgstr "" +msgstr "Rendi stampabile la sporgenza" msgid "Modify the geometry to print overhangs without support material." msgstr "" +"Modificare la geometria per stampare sporgenze senza materiale di supporto." msgid "Make overhang printable maximum angle" -msgstr "" +msgstr "Rendi la sporgenza stampabile angolo massimo" msgid "" "Maximum angle of overhangs to allow after making more steep overhangs " "printable.90° will not change the model at all and allow any overhang, while " "0 will replace all overhangs with conical material." msgstr "" +"Angolo massimo delle sporgenze da consentire dopo aver reso stampabili " +"sporgenze più ripide.90° non cambierà affatto il modello e consentirà alcuna " +"sporgenza, mentre 0 sostituirà tutte le sporgenze con materiale conico." msgid "Make overhang printable hole area" -msgstr "" +msgstr "Rendere stampabile l'area del foro sporgente" msgid "" "Maximum area of a hole in the base of the model before it's filled by " "conical material.A value of 0 will fill all the holes in the model base." msgstr "" +"Area massima di un foro nella base del modello prima che venga riempito da " +"materiale conico. Un valore pari a 0 riempirà tutti i fori nella base del " +"modello." msgid "mm²" msgstr "mm²" msgid "Detect overhang wall" -msgstr "Detect overhang walls" +msgstr "Rileva parete a sbalzo" #, c-format, boost-format msgid "" "Detect the overhang percentage relative to line width and use different " "speed to print. For 100%% overhang, bridge speed is used." msgstr "" -"This detects the overhang percentage relative to line width and uses a " -"different speed to print. For 100%% overhang, bridging speed is used." +"Rileva la percentuale di sporgenza rispetto alla larghezza della linea e " +"utilizza velocità diverse per stampare. Per una sporgenza di 100%%, viene " +"utilizzata la velocità del ponte." msgid "" "Line width of inner wall. If expressed as a %, it will be computed over the " "nozzle diameter." msgstr "" +"Larghezza della linea della parete interna. Se espresso come %, verrà " +"calcolato sul diametro dell'ugello." msgid "Speed of inner wall" -msgstr "This is the speed for inner walls." +msgstr "E' la velocità per le pareti interne." msgid "Number of walls of every layer" -msgstr "This is the number of walls per layer." +msgstr "Questo è il numero di pareti per layer." msgid "" "If you want to process the output G-code through custom scripts, just list " @@ -9324,38 +9825,44 @@ msgid "" "argument, and they can access the Slic3r config settings by reading " "environment variables." msgstr "" +"Se vuoi processare il G-code in uscita con script personalizzati, basta " +"elencare qui il loro percorso assoluto. Separa i diversi script con un punto " +"e virgola. Gli script passeranno il percorso assoluto nel G-code come primo " +"argomento, e potranno accedere alle impostazioni di configurazione di Slic3r " +"leggendo le variabili di ambiente." msgid "Printer notes" -msgstr "" +msgstr "Note stampante" msgid "You can put your notes regarding the printer here." -msgstr "" +msgstr "È possibile inserire qui le note riguardanti la stampante." msgid "Raft contact Z distance" msgstr "Distanza di contatto Z Raft" msgid "Z gap between object and raft. Ignored for soluble interface" msgstr "" -"This is the Z gap between an object and a raft. It is ignored for soluble " -"interfaces." +"È lo spazio Z tra oggetto e raft. Viene ignorato per le interfacce solubili." msgid "Raft expansion" msgstr "Espansione del raft" msgid "Expand all raft layers in XY plane" -msgstr "This expands all raft layers in XY plane." +msgstr "Questo espande tutti i layers del raft nel piano XY." msgid "Initial layer density" -msgstr "First layer density" +msgstr "Densità primo layer" msgid "Density of the first raft or support layer" -msgstr "This is the density of the first raft or support layer." +msgstr "Questa è la densità del raft o del layer di supporto." msgid "Initial layer expansion" -msgstr "First layer expansion" +msgstr "Espansione primo layer" msgid "Expand the first raft or support layer to improve bed plate adhesion" -msgstr "This expands the first raft or support layer to improve bed adhesion." +msgstr "" +"Questo espande il primo raft o layer di supporto per migliorare l'adesione " +"al piatto." msgid "Raft layers" msgstr "Layer raft" @@ -9364,27 +9871,28 @@ msgid "" "Object will be raised by this number of support layers. Use this function to " "avoid wrapping when print ABS" msgstr "" -"Object will be raised by this number of support layers. Use this function to " -"avoid warping when printing ABS." +"L'oggetto verrà sollevato da questo numero di layer di supporto. Utilizzare " +"questa funzione per evitare deformazioni durante la stampa di ABS." msgid "" "G-code path is genereated after simplifing the contour of model to avoid too " "much points and gcode lines in gcode file. Smaller value means higher " "resolution and more time to slice" msgstr "" -"The G-code path is generated after simplifying the contour of models to " -"avoid too many points and G-code lines. Smaller values mean higher " -"resolution and more time required to slice." +"Il percorso del G-code viene generato dopo aver semplificato il contorno del " +"modello per evitare molti punti e linee nel file G-code.\n" +"Un valore più piccolo significa una risoluzione più elevata e un tempo " +"maggiore per l'elaborazione" msgid "Travel distance threshold" -msgstr "Travel distance threshold" +msgstr "Soglia distanza di spostamento" msgid "" "Only trigger retraction when the travel distance is longer than this " "threshold" msgstr "" -"Only trigger retraction when the travel distance is longer than this " -"threshold." +"L'attivazione della retrazione avviene solo quando la distanza percorsa è " +"superiore a questa soglia." msgid "Retract amount before wipe" msgstr "Retrai la quantità prima di pulire" @@ -9392,14 +9900,14 @@ msgstr "Retrai la quantità prima di pulire" msgid "" "The length of fast retraction before wipe, relative to retraction length" msgstr "" -"This is the length of fast retraction before a wipe, relative to retraction " -"length." +"È la lunghezza della retrazione rapida prima di una pulizia, rispetto alla " +"lunghezza di retrazione." msgid "Retract when change layer" -msgstr "Retract on layer change" +msgstr "Ritrai al cambio layer" msgid "Force a retraction when changes layer" -msgstr "This forces a retraction on layer changes." +msgstr "Questo forza una retrazione nei cambi layer." msgid "Length" msgstr "Lunghezza" @@ -9411,24 +9919,25 @@ msgid "" "Some amount of material in extruder is pulled back to avoid ooze during long " "travel. Set zero to disable retraction" msgstr "" -"This is the amount of filament in the extruder that is pulled back to avoid " -"oozing during long travel distances. Set to 0 to disable retraction." +"È la quantità filamento nell'estrusore che viene ritirata per evitare la " +"trasudazione durante le lunghe distanze. Imposta su 0 per disattivare la " +"retrazione." msgid "Z hop when retract" -msgstr "Z hop when retracting" +msgstr "Z hop in fase retrazione" msgid "" "Whenever the retraction is done, the nozzle is lifted a little to create " "clearance between nozzle and the print. It prevents nozzle from hitting the " "print when travel move. Using spiral line to lift z can prevent stringing" msgstr "" -"Whenever there is a retraction, the nozzle is lifted a little to create " -"clearance between the nozzle and the print. This prevents the nozzle from " -"hitting the print when traveling more. Using spiral lines to lift z can " -"prevent stringing." +"Ogni volta che si verifica una retrazione, il nozzle viene sollevato " +"leggermente per creare spazio tra nozzle e stampa. Ciò impedisce al nozzle " +"di colpire la stampa quando si viaggia di più. L'uso di linee a spirale per " +"sollevare z può evitare che si stringano." msgid "Z hop type" -msgstr "" +msgstr "Tipo Z Hop" msgid "Slope" msgstr "Inclinato" @@ -9437,59 +9946,70 @@ msgid "Spiral" msgstr "Spirale" msgid "Only lift Z above" -msgstr "" +msgstr "Solleva Z solo al di sopra" msgid "" "If you set this to a positive value, Z lift will only take place above the " "specified absolute Z." msgstr "" +"Se si imposta questo valore su un valore positivo, l'aumento Z si " +"verificherà solo al di sopra dello Z assoluto specificato." msgid "Only lift Z below" -msgstr "" +msgstr "Solleva Z solo al di sotto" msgid "" "If you set this to a positive value, Z lift will only take place below the " "specified absolute Z." msgstr "" +"Se si imposta questo valore su un valore positivo, l'aumento Z si " +"verificherà solo al di sotto dello Z assoluto specificato." msgid "On surfaces" -msgstr "" +msgstr "Tutte le superfici superiori" msgid "" "Enforce Z Hop behavior. This setting is impacted by the above settings (Only " "lift Z above/below)." msgstr "" +"Applicare il comportamento Z Hop. Questa impostazione è influenzata dalle " +"impostazioni di cui sopra (solo sollevare Z sopra/sotto)." msgid "All Surfaces" -msgstr "" +msgstr "Tutte le Superfici" msgid "Top Only" -msgstr "" +msgstr "Solo sopra" msgid "Bottom Only" -msgstr "" +msgstr "Solo sotto" msgid "Top and Bottom" -msgstr "" +msgstr "Sopra e Sotto" msgid "Extra length on restart" -msgstr "" +msgstr "Lunghezza extra in ripresa" msgid "" "When the retraction is compensated after the travel move, the extruder will " "push this additional amount of filament. This setting is rarely needed." msgstr "" +"Quando la retrazione è compensata dopo un movimento di spostamento, " +"l'estrusore spingerà questa quantità addizionale di filamento. Questa " +"impostazione è raramente necessaria." msgid "" "When the retraction is compensated after changing tool, the extruder will " "push this additional amount of filament." msgstr "" +"Quando la retrazione è compensata dopo un cambio strumento, l'estrusore " +"spingerà questa quantità addizionale di filamento." msgid "Retraction Speed" msgstr "Velocità di retrazione" msgid "Speed of retractions" -msgstr "This is the speed for retraction." +msgstr "E' la velocità di retrazione." msgid "Deretraction Speed" msgstr "Velocità di deretrazione" @@ -9498,25 +10018,28 @@ msgid "" "Speed for reloading filament into extruder. Zero means same speed with " "retraction" msgstr "" -"The speed for reloading filament into the extruder after a retraction; " -"setting this to 0 means that it will be the same speed as the retraction." +"La velocità di ricarica filamento nell'estrusore dopo una retrazione; " +"impostando 0, la velocità sarà la stessa della retrazione." msgid "Use firmware retraction" -msgstr "" +msgstr "Usa retrazione firmware" msgid "" "This experimental setting uses G10 and G11 commands to have the firmware " "handle the retraction. This is only supported in recent Marlin." msgstr "" +"Questa impostazione sperimentale utilizza i comandi G10 e G11 per fare in " +"modo che il firmware gestisca la ritrazione. Questo è supportato solo nel " +"recente Marlin." msgid "Show auto-calibration marks" -msgstr "" +msgstr "Mostra segni di autocalibrazione" msgid "Seam position" -msgstr "Posizione giunzione" +msgstr "Posizione della cucitura" msgid "The start position to print each part of outer wall" -msgstr "This is the starting position for each part of the outer wall." +msgstr "E' la posizione di partenza per ogni parte della parete esterna." msgid "Nearest" msgstr "Più vicino" @@ -9525,21 +10048,23 @@ msgid "Aligned" msgstr "Allineato" msgid "Back" -msgstr "Back" +msgstr "Indietro" msgid "Random" msgstr "Casuale" msgid "Staggered inner seams" -msgstr "" +msgstr "Giunzioni interne sfalsate" msgid "" "This option causes the inner seams to be shifted backwards based on their " "depth, forming a zigzag pattern." msgstr "" +"Questa opzione fa sì che le giunzioni interne vengano spostate all'indietro " +"in base alla loro profondità, formando un motivo a zig-zag." msgid "Seam gap" -msgstr "" +msgstr "Fessura di cucitura" msgid "" "In order to reduce the visibility of the seam in a closed loop extrusion, " @@ -9547,26 +10072,38 @@ msgid "" "This amount can be specified in millimeters or as a percentage of the " "current extruder diameter. The default value for this parameter is 10%." msgstr "" +"Per ridurre la visibilità della cucitura in un'estrusione ad anello chiuso, " +"l'anello viene interrotto e accorciato di una quantità specificata.\n" +"Questa quantità può essere specificata in millimetri o come percentuale del " +"diametro corrente dell'estrusore. Il valore predefinito per questo parametro " +"è 10%." msgid "Role base wipe speed" -msgstr "" +msgstr "Velocità di cancellazione della base dei ruoli" msgid "" "The wipe speed is determined by the speed of the current extrusion role.e.g. " "if a wipe action is executed immediately following an outer wall extrusion, " "the speed of the outer wall extrusion will be utilized for the wipe action." msgstr "" +"La velocità di pulizia è determinata dalla velocità del ruolo di estrusione " +"corrente, ad es. Se un'azione di pulizia viene eseguita immediatamente dopo " +"un'estrusione della parete esterna, la velocità di estrusione della parete " +"esterna verrà utilizzata per l'azione di pulizia." msgid "Wipe on loops" -msgstr "" +msgstr "Pulisci sui loop" msgid "" "To minimize the visibility of the seam in a closed loop extrusion, a small " "inward movement is executed before the extruder leaves the loop." msgstr "" +"Per ridurre al minimo la visibilità della cucitura in un'estrusione ad " +"anello chiuso, viene eseguito un piccolo movimento verso l'interno prima che " +"l'estrusore lasci l'anello." msgid "Wipe speed" -msgstr "" +msgstr "Velocità di pulizia" msgid "" "The wipe speed is determined by the speed setting specified in this " @@ -9574,12 +10111,16 @@ msgid "" "be calculated based on the travel speed setting above.The default value for " "this parameter is 80%" msgstr "" +"La velocità di pulizia è determinata dall'impostazione della velocità " +"specificata in questa configurazione. Se il valore è espresso in percentuale " +"(ad es. 80%), verrà calcolato in base all'impostazione della velocità di " +"traslazione di cui sopra. Il valore predefinito per questo parametro è 80%" msgid "Skirt distance" -msgstr "Skirt distance" +msgstr "Distanza Skirt" msgid "Distance from skirt to brim or object" -msgstr "This is the distance from the skirt to the brim or the object." +msgstr "Questa è la distanza dallo skirt al brim o all'oggetto." msgid "Skirt height" msgstr "Altezza skirt" @@ -9588,45 +10129,49 @@ msgid "How many layers of skirt. Usually only one layer" msgstr "Numero di layer skirt: solitamente uno" msgid "Skirt loops" -msgstr "Skirt loops" +msgstr "Anelli skirt" msgid "Number of loops for the skirt. Zero means disabling skirt" msgstr "" -"This is the number of loops for the skirt. 0 means the skirt is disabled." +"Questo è il numero di loop per lo skirt. 0 indica che lo skirt è disattivata." msgid "Skirt speed" -msgstr "" +msgstr "Velocità del pannello esterno" msgid "Speed of skirt, in mm/s. Zero means use default layer extrusion speed." msgstr "" +"Velocità del gonna, in mm/s. Zero significa utilizzare la velocità di " +"estrusione dello strato predefinita." msgid "" "The printing speed in exported gcode will be slowed down, when the estimated " "layer time is shorter than this value, to get better cooling for these layers" msgstr "" -"The printing speed in exported G-code will be slowed down when the estimated " -"layer time is shorter than this value in order to get better cooling for " -"these layers." +"La velocità di stampa nel G-code esportato verrà rallentata quando il tempo " +"stimato del layer è inferiore a questo valore per ottenere un migliore " +"raffreddamento per questi layers." msgid "Minimum sparse infill threshold" -msgstr "Minimum sparse infill threshold" +msgstr "Soglia minima riempimento" msgid "" "Sparse infill area which is smaller than threshold value is replaced by " "internal solid infill" msgstr "" -"Sparse infill areas which are smaller than this threshold value are replaced " -"by internal solid infill." +"L'area riempimento che è inferiore al valore di soglia viene sostituita da " +"un riempimento solido interno." msgid "" "Line width of internal solid infill. If expressed as a %, it will be " "computed over the nozzle diameter." msgstr "" +"Larghezza della linea del riempimento solido interno. Se espresso come %, " +"verrà calcolato sul diametro dell'ugello." msgid "Speed of internal solid infill, not the top and bottom surface" msgstr "" -"This is the speed for internal solid infill, not including the top or bottom " -"surface." +"E' la velocità del riempimento solido interno, esclusa la superficie " +"superiore o inferiore." msgid "Spiral vase" msgstr "Vaso a spirale" @@ -9636,9 +10181,9 @@ msgid "" "model into a single walled print with solid bottom layers. The final " "generated model has no seam" msgstr "" -"This enables spiraling, which smooths out the Z moves of the outer contour " -"and turns a solid model into a single walled print with solid bottom layers. " -"The final generated model has no seam." +"Consente la stampa a spirale, che attenua i movimenti Z del contorno esterno " +"e trasforma un modello solido in una stampa a parete singola con layers " +"inferiori solidi. Il modello finale generato non presenta alcuna giunzione." msgid "" "If smooth or traditional mode is selected, a timelapse video will be " @@ -9669,19 +10214,19 @@ msgid "Start G-code" msgstr "G-code iniziale" msgid "Start G-code when start the whole printing" -msgstr "G-code added when starting a print" +msgstr "G-code aggiunto all'avvio di una stampa" msgid "Start G-code when start the printing of this filament" -msgstr "G-code added when the printer starts using this filament" +msgstr "G-code aggiunto quando la stampante utilizza questo filamento" msgid "Single Extruder Multi Material" -msgstr "" +msgstr "Estrusore singolo Multi Material" msgid "Use single nozzle to print multi filament" -msgstr "" +msgstr "Usa un ugello singolo per stampare più filamenti" msgid "Manual Filament Change" -msgstr "" +msgstr "Cambio manuale del filamento" msgid "" "Enable this option to omit the custom Change filament G-code only at the " @@ -9690,18 +10235,23 @@ msgid "" "printing, where we use M600/PAUSE to trigger the manual filament change " "action." msgstr "" +"Abilita questa opzione per omettere il codice G Cambia filamento " +"personalizzato solo all'inizio della stampa. Il comando di cambio utensile " +"(ad es. T0) verrà saltato durante l'intera stampa. Ciò è utile per la stampa " +"manuale multi-materiale, in cui utilizziamo M600/PAUSE per attivare l'azione " +"di cambio filamento manuale." msgid "Purge in prime tower" -msgstr "" +msgstr "Spurga nella Prime tower" msgid "Purge remaining filament into prime tower" -msgstr "" +msgstr "Spurgare il filamento rimanente nella torre di prim'ordine" msgid "Enable filament ramming" -msgstr "" +msgstr "Abilita lo speronamento del filamento" msgid "No sparse layers (EXPERIMENTAL)" -msgstr "" +msgstr "Nessun layer sparso (SPERIMENTALE)" msgid "" "If enabled, the wipe tower will not be printed on layers with no " @@ -9709,14 +10259,20 @@ msgid "" "print the wipe tower. User is responsible for ensuring there is no collision " "with the print." msgstr "" +"Se attiva, la torre di pulitura non verrà stampata sui layer senza cambio " +"strumento. Sui layer con un cambio strumento, l'estrusore si sposterà verso " +"il basso per stampare la torre di pulitura. L'utente dovrà accertarsi che " +"non avvengano collisioni con la stampa." msgid "Prime all printing extruders" -msgstr "" +msgstr "Prepara tutti gli estrusori di stampa" msgid "" "If enabled, all printing extruders will be primed at the front edge of the " "print bed at the start of the print." msgstr "" +"Se attivata, tutti gli estrusori di stampa verranno preparati nel bordo " +"frontale del piano di stampa all'inizio della stampa." msgid "Slice gap closing radius" msgstr "Raggio chiusura del gap" @@ -9752,7 +10308,7 @@ msgid "Close holes" msgstr "Chiudi fori" msgid "Z offset" -msgstr "" +msgstr "Offset Z" msgid "" "This value will be added (or subtracted) from all the Z coordinates in the " @@ -9760,12 +10316,17 @@ msgid "" "example, if your endstop zero actually leaves the nozzle 0.3mm far from the " "print bed, set this to -0.3 (or fix your endstop)." msgstr "" +"Questo valore sarà aggiunto (o sottratto) da tutte le coordinate Z nel G-" +"code di output. Viene utilizzato per compensare una posizione di finecorsa Z " +"errata: per esempio, se la posizione minima del finecorsa rimane in realtà " +"0.3mm lontano dal piano, imposta questo valore a -0.3 (o sistema il " +"finecorsa)." msgid "Enable support" -msgstr "Enable support" +msgstr "Abilita supporti" msgid "Enable support generation." -msgstr "This enables support generation." +msgstr "Abilita la generazione dei supporti." msgid "" "normal(auto) and tree(auto) is used to generate support automatically. If " @@ -9780,7 +10341,7 @@ msgid "normal(auto)" msgstr "normal(auto)" msgid "tree(auto)" -msgstr "tree(auto)" +msgstr "albero(auto)" msgid "normal(manual)" msgstr "normale(manuale)" @@ -9789,7 +10350,7 @@ msgid "tree(manual)" msgstr "albero(manuale)" msgid "Support/object xy distance" -msgstr "Support/object xy distance" +msgstr "Distanza xy supporto/oggetto" msgid "XY separation between an object and its support" msgstr "Separazione XY tra un oggetto e il suo supporto" @@ -9798,14 +10359,13 @@ msgid "Pattern angle" msgstr "Angolo trama" msgid "Use this setting to rotate the support pattern on the horizontal plane." -msgstr "" -"Use this setting to rotate the support pattern on the horizontal plane." +msgstr "Usa questo per ruotare sul piano orizzontale la trama del supporto." msgid "On build plate only" -msgstr "On build plate only" +msgstr "Solo dal piatto" msgid "Don't create support on model surface, only on build plate" -msgstr "This setting only generates supports that begin on the build plate." +msgstr "Questa impostazione genera solo i supporti che poggiano sul piatto." msgid "Support critical regions only" msgstr "Supporta solo aree critiche" @@ -9818,16 +10378,18 @@ msgstr "" "tagliente, sbalzo, ecc." msgid "Remove small overhangs" -msgstr "" +msgstr "Rimuovere piccole sporgenze" msgid "Remove small overhangs that possibly need no supports." msgstr "" +"Rimuovere le piccole sporgenze che eventualmente non necessitano di supporti." msgid "Top Z distance" -msgstr "Top Z distance" +msgstr "Distanza Z superiore" msgid "The z gap between the top support interface and object" -msgstr "This determines the Z gap between top support interfaces and objects." +msgstr "" +"Determina lo spazio Z gap tra le interfacce supporto superiori e gli oggetti." msgid "Bottom Z distance" msgstr "Distanza Z inferiore" @@ -9850,6 +10412,8 @@ msgid "" "Line width of support. If expressed as a %, it will be computed over the " "nozzle diameter." msgstr "" +"Larghezza della linea di supporto. Se espresso come %, verrà calcolato sul " +"diametro dell'ugello." msgid "Interface use loop pattern" msgstr "Loop pattern interface" @@ -9875,32 +10439,31 @@ msgid "Top interface layers" msgstr "Layer superiori di interfaccia " msgid "Number of top interface layers" -msgstr "This is the number of top interface layers." +msgstr "È il numero di layer di interfaccia superiore." msgid "Bottom interface layers" msgstr "Layer inferiori di interfaccia " msgid "Top interface spacing" -msgstr "Top interface spacing" +msgstr "Spaziatura interfaccia superiore" msgid "Spacing of interface lines. Zero means solid interface" -msgstr "This is the spacing of interface lines. 0 means solid interface." +msgstr "Spaziatura linee interfaccia. 0 significa interfaccia solida" msgid "Bottom interface spacing" -msgstr "Bottom interface spacing" +msgstr "Spaziatura inferiore interfaccia" msgid "Spacing of bottom interface lines. Zero means solid interface" -msgstr "" -"This is the spacing of bottom interface lines. 0 means solid interface." +msgstr "Spaziatura linee interfaccia di fondo. 0 significa interfaccia solida" msgid "Speed of support interface" -msgstr "This is the speed for support interfaces." +msgstr "E' la velocità per le interfacce di supporto." msgid "Base pattern" -msgstr "Base pattern" +msgstr "Trama base" msgid "Line pattern of support" -msgstr "This is the line pattern for support." +msgstr "Questo è la trama lineare del supporto." msgid "Rectilinear grid" msgstr "Griglia rettilinea" @@ -9916,18 +10479,18 @@ msgid "" "interface is Rectilinear, while default pattern for soluble support " "interface is Concentric" msgstr "" -"This is the line pattern for support interfaces. The default pattern for non-" -"soluble support interfaces is Rectilinear while the default pattern for " -"soluble support interfaces is Concentric." +"Questo è la Trama lineare per le interfacce di supporto. Il modello " +"predefinito per le interfacce di supporto non solubili è rettilineo mentre " +"il modello predefinito per le interfacce di supporto solubili è concentrico." msgid "Rectilinear Interlaced" msgstr "Rettilineo Interlacciato" msgid "Base pattern spacing" -msgstr "Base pattern spacing" +msgstr "Spazio trama base" msgid "Spacing between support lines" -msgstr "This determines the spacing between support lines." +msgstr "Questo determina la spaziatura tra le linee di supporto." msgid "Normal Support expansion" msgstr "Espansione normale dei supporti" @@ -9937,7 +10500,7 @@ msgstr "" "Espandere (+) o restringere (-) la portata orizzontale del supporto normale" msgid "Speed of support" -msgstr "This is the speed for support." +msgstr "E' la velocità del supporto." msgid "" "Style and shape of the support. For normal support, projecting the supports " @@ -9948,6 +10511,14 @@ msgid "" "style will create similar structure to normal support under large flat " "overhangs." msgstr "" +"Stile e forma del supporto. Per il supporto normale, proiettare i supporti " +"in una griglia regolare creerà supporti più stabili (impostazione " +"predefinita), mentre le torri di supporto aderenti risparmieranno materiale " +"e ridurranno le cicatrici degli oggetti.\n" +"Per il supporto dell'albero, lo stile sottile e organico fonderà i rami in " +"modo più aggressivo e risparmierà molto materiale (organico predefinito), " +"mentre lo stile ibrido creerà una struttura simile al supporto normale sotto " +"grandi sporgenze piatte." msgid "Snug" msgstr "Aderenti" @@ -9956,16 +10527,16 @@ msgid "Tree Slim" msgstr "Albero Slim" msgid "Tree Strong" -msgstr "Albero Strong" +msgstr "Albero Forte" msgid "Tree Hybrid" msgstr "Albero ibrido" msgid "Organic" -msgstr "" +msgstr "Organico" msgid "Independent support layer height" -msgstr "Independent support layer height" +msgstr "Altezza layer di supporto indipendente" msgid "" "Support layer uses layer height independent with object layer. This is to " @@ -9978,29 +10549,30 @@ msgstr "" "la Prime Tower è abilitata." msgid "Threshold angle" -msgstr "Threshold angle" +msgstr "Angolo di soglia" msgid "" "Support will be generated for overhangs whose slope angle is below the " "threshold." msgstr "" -"Support will be generated for overhangs whose slope angle is below this " -"threshold." +"Il supporto sarà generato per le sporgenze il cui angolo di inclinazione è " +"inferiore alla soglia." msgid "Tree support branch angle" -msgstr "Tree support branch angle" +msgstr "Angolo ramo supporti ad albero" msgid "" "This setting determines the maximum overhang angle that t he branches of " "tree support allowed to make.If the angle is increased, the branches can be " "printed more horizontally, allowing them to reach farther." msgstr "" -"This setting determines the maximum overhang angle that the branches of tree " -"support are allowed to make. If the angle is increased, the branches can be " -"printed more horizontally, allowing them to reach farther." +"Questa determina l'angolo massimo di sporgenza che i rami del supporto ad " +"albero possono raggiungere. Se l'angolo viene aumentato, i rami possono " +"essere stampati più orizzontalmente, permettendo loro di arrivare più " +"lontano." msgid "Preferred Branch Angle" -msgstr "" +msgstr "Angolo di diramazione preferito" #. TRN PrintSettings: "Organic supports" > "Preferred Branch Angle" msgid "" @@ -10008,17 +10580,21 @@ msgid "" "model. Use a lower angle to make them more vertical and more stable. Use a " "higher angle for branches to merge faster." msgstr "" +"L'angolo di inclinazione preferito delle ramificazioni, quando non devono " +"evitare il modello. Utilizzare un angolo più basso per renderli più " +"verticali e più stabili. Utilizzare un angolo più alto per far sì che le " +"ramificazioni si uniscano più velocemente." msgid "Tree support branch distance" -msgstr "Tree support branch distance" +msgstr "Distanza ramo supporti ad albero" msgid "" "This setting determines the distance between neighboring tree support nodes." msgstr "" -"This setting determines the distance between neighboring tree support nodes." +"Questa determina la distanza tra i nodi di supporto dell'albero vicini." msgid "Branch Density" -msgstr "" +msgstr "Densità Ramificazioni" #. TRN PrintSettings: "Organic supports" > "Branch Density" msgid "" @@ -10028,45 +10604,55 @@ msgid "" "interfaces instead of a high branch density value if dense interfaces are " "needed." msgstr "" +"Regola la densità della struttura di supporto utilizzata per generare le " +"punte delle ramificazioni. Un valore più alto produce sporgenze migliori, ma " +"i supporti sono più difficili da rimuovere; si consiglia quindi di abilitare " +"l'interfaccia di supporto superiore invece di impostare un valore elevato di " +"densità delle ramificazioni, se sono necessarie interfacce dense." msgid "Adaptive layer height" -msgstr "Adaptive layer height" +msgstr "Altezza del livello adattivo" msgid "" "Enabling this option means the height of tree support layer except the " "first will be automatically calculated " msgstr "" +"L'attivazione di questa opzione significa che l'altezza del layer di " +"supporto dell'albero, ad eccezione del primo, verrà calcolata " +"automaticamente " msgid "Auto brim width" -msgstr "" +msgstr "Larghezza automatica del bordo" msgid "" "Enabling this option means the width of the brim for tree support will be " "automatically calculated" msgstr "" +"Abilitando questa opzione, la larghezza della tesa per il supporto " +"dell'albero verrà calcolata automaticamente" msgid "Tree support brim width" msgstr "Larghezza brim supporto ad albero" msgid "Distance from tree branch to the outermost brim line" -msgstr "" +msgstr "Distanza dal ramo dell'albero alla linea di bordo più esterna" msgid "Tip Diameter" -msgstr "" +msgstr "Diametro della punta" #. TRN PrintSettings: "Organic supports" > "Tip Diameter" msgid "Branch tip diameter for organic supports." -msgstr "" +msgstr "Diametro della punta delle ramificazioni per i supporti organici." msgid "Tree support branch diameter" -msgstr "Tree support branch diameter" +msgstr "Diametro ramo supporti ad albero" msgid "This setting determines the initial diameter of support nodes." -msgstr "This setting determines the initial diameter of support nodes." +msgstr "Questa determina il diametro iniziale dei nodi di supporto." #. TRN PrintSettings: #lmFIXME msgid "Branch Diameter Angle" -msgstr "" +msgstr "Angolo del diametro della diramazione" #. TRN PrintSettings: "Organic supports" > "Branch Diameter Angle" msgid "" @@ -10075,9 +10661,13 @@ msgid "" "over their length. A bit of an angle can increase stability of the organic " "support." msgstr "" +"L'angolo del diametro delle ramificazioni che diventano gradualmente più " +"spesse verso il basso. Un angolo pari a 0 fa sì che le ramificazioni abbiano " +"uno spessore uniforme per tutta la loro lunghezza. Un angolo un po' più " +"ampio può aumentare la stabilità del supporto organico." msgid "Branch Diameter with double walls" -msgstr "" +msgstr "Diametro diramazioni con pareti doppie" #. TRN PrintSettings: "Organic supports" > "Branch Diameter" msgid "" @@ -10085,34 +10675,40 @@ msgid "" "printed with double walls for stability. Set this value to zero for no " "double walls." msgstr "" +"Le ramificazioni con un'area superiore all'area di un cerchio di questo " +"diametro verranno stampate con pareti doppie per garantire la stabilità. " +"Imposta questo valore a zero per non avere pareti doppie." msgid "Tree support wall loops" -msgstr "Tree support wall loops" +msgstr "Loop parete supporto ad albero" msgid "This setting specify the count of walls around tree support" -msgstr "This setting specifies the wall count around tree support." +msgstr "Questa specifica il numero di pareti attorno al supporto ad albero." msgid "Tree support with infill" -msgstr "Tree support with infill" +msgstr "Riempimento supporti ad albero" msgid "" "This setting specifies whether to add infill inside large hollows of tree " "support" msgstr "" -"This setting specifies whether to add infill inside large hollows of tree " -"support." +"Questa impostazione specifica se aggiungere il riempimento all'interno di " +"grandi cavità del supporto dell'albero" msgid "Activate temperature control" -msgstr "" +msgstr "Attiva il controllo della temperatura" msgid "" "Enable this option for chamber temperature control. An M191 command will be " "added before \"machine_start_gcode\"\n" "G-code commands: M141/M191 S(0-255)" msgstr "" +"Abilitare questa opzione per il controllo della temperatura della camera. Un " +"comando M191 verrà aggiunto prima di \"machine_start_gcode\"\n" +"Comandi G-code: M141/M191 S(0-255)" msgid "Chamber temperature" -msgstr "" +msgstr "Temperatura della camera di stampa" msgid "" "Higher chamber temperature can help suppress or reduce warping and " @@ -10123,55 +10719,68 @@ msgid "" "high to avoid cloggings, so 0 which stands for turning off is highly " "recommended" msgstr "" +"Una temperatura della camera più elevata può aiutare a sopprimere o ridurre " +"la deformazione e potenzialmente portare a una maggiore forza di adesione " +"tra gli strati per materiali ad alta temperatura come ABS, ASA, PC, PA e " +"così via. Allo stesso tempo, la filtrazione dell'aria di ABS e ASA " +"peggiorerà. Mentre per PLA, PETG, TPU, PVA e altri materiali a bassa " +"temperatura, la temperatura effettiva della camera non dovrebbe essere " +"elevata per evitare intasamenti, quindi 0 che sta per spegnimento è " +"altamente raccomandato" msgid "Nozzle temperature for layers after the initial one" -msgstr "Nozzle temperature after the first layer" +msgstr "Temperatura del nozzle dopo il primo layer" msgid "Detect thin wall" -msgstr "Detect thin walls" +msgstr "Rileva pareti sottili" msgid "" "Detect thin wall which can't contain two line width. And use single line to " "print. Maybe printed not very well, because it's not closed loop" msgstr "" -"This detects thin walls which can’t contain two lines and uses a single line " -"to print. It may not print as well because it’s not a closed loop." +"Questo rileva pareti sottili che non possono contenere due righe e utilizza " +"una sola riga per la stampa. Potrebbe non essere stampato altrettanto bene " +"perché non è un circuito chiuso." msgid "" "This gcode is inserted when change filament, including T command to trigger " "tool change" msgstr "" -"This G-code is inserted when filament is changed, including T commands to " -"trigger tool change." +"Questo G-code viene inserito al cambio filamento, compresi i comandi T per " +"attivare il cambio utensile." msgid "This gcode is inserted when the extrusion role is changed" msgstr "" +"Questo gcode viene inserito quando viene modificato il ruolo di estrusione" msgid "" "Line width for top surfaces. If expressed as a %, it will be computed over " "the nozzle diameter." msgstr "" +"Larghezza della linea per le superfici superiori. Se espresso come %, verrà " +"calcolato sul diametro dell'ugello." msgid "Speed of top surface infill which is solid" -msgstr "This is the speed for solid top surface infill." +msgstr "E' la velocità per il riempimento superficie superiore solida." msgid "Top shell layers" -msgstr "Top shell layers" +msgstr "Layer guscio superiore" msgid "" "This is the number of solid layers of top shell, including the top surface " "layer. When the thickness calculated by this value is thinner than top shell " "thickness, the top shell layers will be increased" msgstr "" -"This is the number of solid layers of top shell, including the top surface " -"layer. When the thickness calculated by this value is thinner than the top " -"shell thickness, the top shell layers will be increased" +"È il numero di layers solidi del guscio superiore, compreso il layer " +"superficiale superiore. Se lo spessore calcolato con questo valore è più " +"sottile dello spessore del guscio superiore, i layers del guscio superiore " +"vengono aumentati." msgid "Top solid layers" msgstr "Layer solidi superiori" msgid "Top shell thickness" -msgstr "Top shell thickness" +msgstr "Spessore guscio superiore" msgid "" "The number of top solid layers is increased when slicing if the thickness " @@ -10180,14 +10789,15 @@ msgid "" "is disabled and thickness of top shell is absolutely determained by top " "shell layers" msgstr "" -"The number of top solid layers is increased when slicing if the thickness " -"calculated by top shell layers is thinner than this value. This can avoid " -"having too thin a shell when layer height is small. 0 means that this " -"setting is disabled and thickness of top shell is determined simply by the " -"number of top shell layers." +"Il numero di layers solidi superiori aumenta durante l'elaborazione se lo " +"spessore calcolato dai layers superiori del guscio è più sottile di questo " +"valore. Questo può evitare di avere un guscio troppo sottile quando " +"l'altezza del layer è ridotta. 0 significa che questa impostazione è " +"disabilitata e lo spessore del guscio superiore è determinato semplicemente " +"dal numero di layers superiori del guscio." msgid "Speed of travel which is faster and without extrusion" -msgstr "This is the speed at which traveling is done." +msgstr "È la velocità con cui si viaggia." msgid "Wipe while retracting" msgstr "Pulisci durante la retrazione" @@ -10196,18 +10806,19 @@ msgid "" "Move nozzle along the last extrusion path when retracting to clean leaked " "material on nozzle. This can minimize blob when print new part after travel" msgstr "" -"This moves the nozzle along the last extrusion path when retracting to clean " -"any leaked material on the nozzle. This can minimize blobs when printing a " -"new part after traveling." +"Questo sposta il nozzle lungo l'ultimo percorso di estrusione quando si " +"ritrae per pulire il materiale fuoriuscito dal nozzle. In questo modo è " +"possibile ridurre al minimo i blob quando si stampa una nuova parte dopo lo " +"spostamento." msgid "Wipe Distance" -msgstr "Wipe distance" +msgstr "Distanza pulizia" msgid "" "Discribe how long the nozzle will move along the last path when retracting" msgstr "" -"This describes how long the nozzle will move along the last path while " -"retracting." +"Descrive per quanto tempo il nozzle si muoverà lungo l'ultimo percorso " +"mentre si ritrae." msgid "" "The wiping tower can be used to clean up the residue on the nozzle and " @@ -10222,7 +10833,7 @@ msgid "Purging volumes" msgstr "Volumi di spurgo" msgid "Flush multiplier" -msgstr "Flush multiplier" +msgstr "Moltiplicatore spurgo" msgid "" "The actual flushing volumes is equal to the flush multiplier multiplied by " @@ -10232,54 +10843,61 @@ msgstr "" "moltiplicato per i volumi di spurgo indicati nella tabella." msgid "Prime volume" -msgstr "Prime volume" +msgstr "Volume primario" msgid "The volume of material to prime extruder on tower." -msgstr "" -"This is the volume of material to prime the extruder with on the tower." +msgstr "E' il volume materiale da usare per la Prime Tower" msgid "Width" msgstr "Larghezza" msgid "Width of prime tower" -msgstr "This is the width of prime towers." +msgstr "È la larghezza della Prime Tower." msgid "Wipe tower rotation angle" -msgstr "" +msgstr "Angolo di rotazione della torre di pulitura" msgid "Wipe tower rotation angle with respect to x-axis." -msgstr "" +msgstr "Angolo di rotazione della torre di pulitura rispetto all'asse X." msgid "Stabilization cone apex angle" -msgstr "" +msgstr "Angolo del cono di stabilizzazione" msgid "" "Angle at the apex of the cone that is used to stabilize the wipe tower. " "Larger angle means wider base." msgstr "" +"Angolo all'apice del cono utilizzato per stabilizzare la torre di pulitura. " +"Un angolo maggiore significa una base più ampia." msgid "Wipe tower purge lines spacing" -msgstr "" +msgstr "Spaziatura delle linee di spurgo della torre di pulitura" msgid "Spacing of purge lines on the wipe tower." -msgstr "" +msgstr "Spaziatura delle linee di spurgo sulla torre di pulitura." msgid "Wipe tower extruder" -msgstr "" +msgstr "Estrusore torre di pulitura" msgid "" "The extruder to use when printing perimeter of the wipe tower. Set to 0 to " "use the one that is available (non-soluble would be preferred)." msgstr "" +"L'estrusore da utilizzare per la stampa del perimetro della torre di " +"pulitura. Impostare su 0 per utilizzare quello attualmente disponibile " +"(sarebbe preferibile quello non solubile)." msgid "Purging volumes - load/unload volumes" -msgstr "" +msgstr "Volumi di spurgo - volumi di carico/scarico" msgid "" "This vector saves required volumes to change from/to each tool used on the " "wipe tower. These values are used to simplify creation of the full purging " "volumes below." msgstr "" +"Questo vettore salva il volume necessario per cambiare da/a ogni strumento " +"usato per la torre di pulitura. Questi valori vengono usati per semplificare " +"la creazione dei volumi di spurgo completi." msgid "" "Purging after filament change will be done inside objects' infills. This may " @@ -10314,26 +10932,27 @@ msgstr "" "Prime Tower." msgid "Maximal bridging distance" -msgstr "" +msgstr "Distanza massima bridging" msgid "Maximal distance between supports on sparse infill sections." -msgstr "" +msgstr "Distanza massima tra supporti in sezioni a riempimento sparso." msgid "X-Y hole compensation" -msgstr "X-Y hole compensation" +msgstr "Compensazione foro X-Y" msgid "" "Holes of object will be grown or shrunk in XY plane by the configured value. " "Positive value makes holes bigger. Negative value makes holes smaller. This " "function is used to adjust size slightly when the object has assembling issue" msgstr "" -"Holes in objects will be grown or shrunk in the XY plane by the set value. " -"Positive values make holes bigger and negative values make holes smaller. " -"This function is used to adjust size slightly when objects have assembly " -"issues." +"I fori negli oggetti vengono ingranditi o rimpiccioliti nel piano XY in base " +"al valore impostato. Un valore positivo ingrandisce i fori mentre un valore " +"negativo rimpicciolisce i fori. Questa funzione viene utilizzata per " +"regolare leggermente le dimensioni quando gli oggetti presentano problemi di " +"assemblaggio." msgid "X-Y contour compensation" -msgstr "X-Y contour compensation" +msgstr "Compensazione contorni X-Y" msgid "" "Contour of object will be grown or shrunk in XY plane by the configured " @@ -10341,13 +10960,14 @@ msgid "" "smaller. This function is used to adjust size slightly when the object has " "assembling issue" msgstr "" -"The contour of objects will be grown or shrunk in the XY plane by the set " -"value. Positive values make contours bigger, and negative values make " -"contours smaller. This function is used to adjust sizes slightly when " -"objects have assembly issues." +"Il contorno degli oggetti viene ingrandito o rimpicciolito nel piano XY in " +"base al valore impostato. I valori positivi ingrandiscono i contorni e " +"quelli negativi li rimpiccioliscono. Questa funzione viene utilizzata per " +"regolare leggermente le dimensioni quando gli oggetti presentano problemi di " +"assemblaggio." msgid "Convert holes to polyholes" -msgstr "" +msgstr "Conversione di fori in polifori" msgid "" "Search for almost-circular holes that span more than one layer and convert " @@ -10355,9 +10975,13 @@ msgid "" "compute the polyhole.\n" "See http://hydraraptor.blogspot.com/2011/02/polyholes.html" msgstr "" +"Cercare fori quasi circolari che si estendono su più di un layer e " +"convertire la geometria in polifori. Utilizzare la dimensione dell'ugello e " +"il diametro (più grande) per calcolare il poliforo.\n" +"Vedi http://hydraraptor.blogspot.com/2011/02/polyholes.html" msgid "Polyhole detection margin" -msgstr "" +msgstr "Margine di rilevamento poliforo" #, c-format, boost-format msgid "" @@ -10367,31 +10991,40 @@ msgid "" "broaden the detection.\n" "In mm or in % of the radius." msgstr "" +"Defezione massima di un punto rispetto al raggio stimato del cerchio.\n" +"Poiché i cilindri vengono spesso esportati come triangoli di dimensioni " +"variabili, i punti potrebbero non trovarsi sulla circonferenza del cerchio. " +"Questa impostazione consente di ampliare il rilevamento.\n" +"In mm o in % of il raggio." msgid "Polyhole twist" -msgstr "" +msgstr "Torsione poliforo" msgid "Rotate the polyhole every layer." -msgstr "" +msgstr "Ruotare il poliforo in ogni strato." msgid "G-code thumbnails" -msgstr "" +msgstr "Miniature G-code" msgid "" "Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the " "following format: \"XxY, XxY, ...\"" msgstr "" +"Dimensioni delle immagini da memorizzare in file .gcode e .sl1 / .sl1s, nel " +"seguente formato: \"XxY, XxY, ...\"" msgid "Format of G-code thumbnails" -msgstr "" +msgstr "Formato miniature del G-code" msgid "" "Format of G-code thumbnails: PNG for best quality, JPG for smallest size, " "QOI for low memory firmware" msgstr "" +"Formato delle miniature del G-code: PNG per la migliore qualità, JPG per la " +"dimensione più piccola, QOI per il firmware con poca memoria" msgid "Use relative E distances" -msgstr "" +msgstr "Usa distanze E relative" msgid "" "Relative extrusion is recommended when using \"label_objects\" option.Some " @@ -10399,6 +11032,11 @@ msgid "" "Wipe tower is only compatible with relative mode. It is always enabled on " "BambuLab printers. Default is checked" msgstr "" +"L'estrusione relativa è consigliata quando si utilizza l'opzione " +"\"label_objects\". Alcuni estrusori funzionano meglio con questa opzione " +"unckecked (modalità di estrusione assoluta). La torre di pulizia è " +"compatibile solo con la modalità relativa. È sempre abilitato sulle " +"stampanti BambuLab. Il valore predefinito è selezionato" msgid "" "Classic wall generator produces walls with constant extrusion width and for " @@ -10492,13 +11130,16 @@ msgstr "" "espresso in percentuale rispetto al diametro del nozzle" msgid "First layer minimum wall width" -msgstr "" +msgstr "Larghezza minima della parete del primo strato" msgid "" "The minimum wall width that should be used for the first layer is " "recommended to be set to the same size as the nozzle. This adjustment is " "expected to enhance adhesion." msgstr "" +"Si consiglia di impostare la larghezza minima della parete da utilizzare per " +"il primo strato alla stessa dimensione dell'ugello. Si prevede che questo " +"aggiustamento migliorerà l'adesione." msgid "Minimum wall width" msgstr "Larghezza minima parete" @@ -10516,16 +11157,19 @@ msgstr "" "diametro del nozzle" msgid "Detect narrow internal solid infill" -msgstr "Detect narrow internal solid infill" +msgstr "Rileva riempimento solido interno stretto" + msgid "" "This option will auto detect narrow internal solid infill area. If enabled, " "concentric pattern will be used for the area to speed printing up. " "Otherwise, rectilinear pattern is used defaultly." msgstr "" -"This option will auto-detect narrow internal solid infill areas. If enabled, " -"the concentric pattern will be used for the area to speed up printing. " -"Otherwise, the rectilinear pattern will be used by default." +"Questa rileva automaticamente le aree interne strette di riempimento solido. " +"Se abilitato, la trama concentrica verrà utilizzato per l'area per " +"velocizzare la stampa. Altrimenti, la trama rettilinea verrà utilizzata per " +"impostazione predefinita." + msgid "invalid value " msgstr "Valore non valido" @@ -10547,7 +11191,7 @@ msgid "Export 3MF" msgstr "Esporta 3MF" msgid "Export project as 3MF." -msgstr "This exports the project as a 3mf file." +msgstr "Questo esporta il progetto come file 3mf." msgid "Export slicing data" msgstr "Esporta dati elaborati" @@ -10562,19 +11206,19 @@ msgid "Load cached slicing data from directory" msgstr "Carica i dati di slicing nella cache dalla directory" msgid "Export STL" -msgstr "" +msgstr "Esporta STL" msgid "Export the objects as multiple STL." -msgstr "" +msgstr "Esportare gli oggetti come STL multipli." msgid "Slice" -msgstr "Processa" +msgstr "Slice" msgid "Slice the plates: 0-all plates, i-plate i, others-invalid" -msgstr "Slice the plates: 0-all plates, i-plate i, others-invalid" +msgstr "Slicing dei piatti: 0-tutti i piatti, i-piatto i, altri-invalidi" msgid "Show command help." -msgstr "This shows command help." +msgstr "Mostra la guida ai comandi." msgid "UpToDate" msgstr "Aggiornato" @@ -10583,16 +11227,16 @@ msgid "Update the configs values of 3mf to latest." msgstr "Aggiorna valori di configurazione dei 3mf ai più recenti." msgid "Load default filaments" -msgstr "" +msgstr "Carica filamenti predefiniti" msgid "Load first filament as default for those not loaded" -msgstr "" +msgstr "Carica il primo filamento come predefinito per quelli non caricati" msgid "Minimum save" -msgstr "" +msgstr "Salvataggio minimo" msgid "export 3mf with minimum size." -msgstr "" +msgstr "Esporta 3MF con dimensione minima." msgid "mtcpp" msgstr "mtcpp" @@ -10624,13 +11268,13 @@ msgid "Output Model Info" msgstr "Info Modello di output" msgid "Output the model's information." -msgstr "This outputs the model’s information." +msgstr "Questo produce le informazioni del modello." msgid "Export Settings" -msgstr "Export Settings" +msgstr "Esporta impostazioni" msgid "Export settings to a file." -msgstr "This exports settings to a file." +msgstr "Questo esporta le impostazioni in un file." msgid "Send progress to pipe" msgstr "Inviare l'avanzamento al pipe" @@ -10639,65 +11283,67 @@ msgid "Send progress to pipe." msgstr "Inviare l'avanzamento al pipe" msgid "Arrange Options" -msgstr "Arrange Options" +msgstr "Opzioni disposizione" msgid "Arrange options: 0-disable, 1-enable, others-auto" -msgstr "Arrange options: 0-disable, 1-enable, others-auto" +msgstr "Opzioni di disposizione: 0-disabilita, 1-abilita, altro-auto" msgid "Repetions count" -msgstr "" +msgstr "Conteggio delle ripetizioni" msgid "Repetions count of the whole model" -msgstr "" +msgstr "Numero di ripetizioni dell'intero modello" msgid "Ensure on bed" -msgstr "" +msgstr "Accerta che sia sul piano" msgid "" "Lift the object above the bed when it is partially below. Disabled by default" msgstr "" +"Sollevare l'oggetto sopra il letto quando è parzialmente sotto. Disabilitato " +"per impostazione predefinita" msgid "Convert Unit" -msgstr "Convert Unit" +msgstr "Converti unità" msgid "Convert the units of model" -msgstr "Convert the units of model" +msgstr "Converti le unità del modello" msgid "Orient Options" -msgstr "" +msgstr "Opzioni di orientamento" msgid "Orient options: 0-disable, 1-enable, others-auto" -msgstr "" +msgstr "Opzioni di orientamento: 0-disabilita, 1-abilita, altri-auto" msgid "Rotation angle around the Z axis in degrees." -msgstr "" +msgstr "Angolo di rotazione attorno all'asse Z in gradi." msgid "Rotate around X" -msgstr "" +msgstr "Ruota attorno ad X" msgid "Rotation angle around the X axis in degrees." -msgstr "" +msgstr "Angolo di rotazione attorno all'asse X in gradi." msgid "Rotate around Y" -msgstr "" +msgstr "Ruota attorno ad Y" msgid "Rotation angle around the Y axis in degrees." -msgstr "" +msgstr "Angolo di rotazione sull'asse Y in gradi." msgid "Scale the model by a float factor" -msgstr "Scale the model by a float factor" +msgstr "Ridimensiona il modello in base a un fattore float" msgid "Load General Settings" -msgstr "Load General Settings" +msgstr "Carica impostazioni generali" msgid "Load process/machine settings from the specified file" -msgstr "Load process/machine settings from the specified file" +msgstr "Carica le impostazioni di processo/macchina dal file specificato" msgid "Load Filament Settings" -msgstr "Load Filament Settings" +msgstr "Carica impostazioni filamento" msgid "Load filament settings from the specified file list" -msgstr "Load filament settings from the specified file list" +msgstr "Carica le impostazioni del filamento dall'elenco di file specificato" msgid "Skip Objects" msgstr "Salta oggetti" @@ -10707,63 +11353,69 @@ msgstr "Salta alcuni oggetti in questa stampa" msgid "load uptodate process/machine settings when using uptodate" msgstr "" +"Caricare le impostazioni di processo/macchina aggiornate quando si utilizza " +"UptoDate" msgid "" "load uptodate process/machine settings from the specified file when using " "uptodate" msgstr "" +"Caricare le impostazioni di processo/macchina aggiornate dal file " +"specificato quando si utilizza UptoDate" msgid "Data directory" -msgstr "" +msgstr "Directory dati" msgid "" "Load and store settings at the given directory. This is useful for " "maintaining different profiles or including configurations from a network " "storage." msgstr "" +"Carica e archivia le impostazione in una data cartella. Questo è utile per " +"mantenere diversi profili o aggiungere configurazioni da un archivio di rete." msgid "Output directory" msgstr "Output directory" msgid "Output directory for the exported files." -msgstr "This is the output directory for exported files." +msgstr "Questa è la cartella di destinazione per i file esportati." msgid "Debug level" -msgstr "Debug level" +msgstr "Livello di debug" msgid "" "Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" "trace\n" msgstr "" -"Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" +"Imposta livello di debug. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" "trace\n" msgid "Load custom gcode" -msgstr "" +msgstr "Carica gcode personalizzato" msgid "Load custom gcode from json" -msgstr "" +msgstr "Carica gcode personalizzato da json" msgid "Error in zip archive" -msgstr "Error in zip archive" +msgstr "Errore nell'archivio zip" msgid "Generating walls" -msgstr "Generating walls" +msgstr "Generazione pareti" msgid "Generating infill regions" -msgstr "Generating infill regions" +msgstr "Generazione regioni di riempimento" msgid "Generating infill toolpath" -msgstr "Generating infill toolpath" +msgstr "Generazione percorso utensile di riempimento" msgid "Detect overhangs for auto-lift" msgstr "Rilevare le sporgenze per il sollevamento automatico" msgid "Generating support" -msgstr "Generating support" +msgstr "Generazione supporto" msgid "Checking support necessity" -msgstr "Checking support necessity" +msgstr "Verifica necessità di supporto" msgid "floating regions" msgstr "regioni galleggianti" @@ -10783,7 +11435,7 @@ msgstr "" "generazione dei supporti." msgid "Optimizing toolpath" -msgstr "Optimizing toolpath" +msgstr "Ottimizzazione del percorso utensile" msgid "Slicing mesh" msgstr "Slicing mesh" @@ -10807,34 +11459,34 @@ msgstr "" #, c-format, boost-format msgid "Support: generate toolpath at layer %d" -msgstr "Support: generate toolpath at layer %d" +msgstr "Supporto: generazione percorso utensile al layer %d" msgid "Support: detect overhangs" -msgstr "Support: detect overhangs" +msgstr "Supporto: rilevamento sporgenze" msgid "Support: generate contact points" -msgstr "Support: generate contact points" +msgstr "Supporto: generazione punti di contatto" msgid "Support: propagate branches" -msgstr "Support: propagate branches" +msgstr "Supporto: propagazione rami" msgid "Support: draw polygons" -msgstr "Support: draw polygons" +msgstr "Supporto: disegno poligoni" msgid "Support: generate toolpath" -msgstr "Support: generate toolpath" +msgstr "Supporto: generazione percorso utensile" #, c-format, boost-format msgid "Support: generate polygons at layer %d" -msgstr "Support: generate polygons at layer %d" +msgstr "Supporto: generazione poligoni al layer %d" #, c-format, boost-format msgid "Support: fix holes at layer %d" -msgstr "Support: fix holes at layer %d" +msgstr "Supporto: correzione dei buchi nel layer %d" #, c-format, boost-format msgid "Support: propagate branches at layer %d" -msgstr "Support: propagate branches at layer %d" +msgstr "Supporto: propagazione rami al layer %d" msgid "" "Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension." @@ -10872,70 +11524,75 @@ msgid "This OBJ file couldn't be read because it's empty." msgstr "Impossibile leggere il file OBJ perché è vuoto." msgid "Flow Rate Calibration" -msgstr "" +msgstr "Calibrazione della portata" msgid "Max Volumetric Speed Calibration" -msgstr "" +msgstr "Calibrazione della velocità volumetrica massima" msgid "Manage Result" -msgstr "" +msgstr "Gestisci risultato" msgid "Manual Calibration" -msgstr "" +msgstr "Calibrazione manuale" msgid "Result can be read by human eyes." -msgstr "" +msgstr "Il risultato può essere letto da occhi umani." msgid "Auto-Calibration" -msgstr "" +msgstr "Calibrazione automatica" msgid "We would use Lidar to read the calibration result" -msgstr "" +msgstr "Utilizzeremmo il Lidar per leggere il risultato della calibrazione" msgid "Prev" -msgstr "" +msgstr "Precedente" msgid "Recalibration" -msgstr "" +msgstr "Ricalibrazione" msgid "Calibrate" -msgstr "" +msgstr "Calibra" msgid "Finish" msgstr "Fine" msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "How to use calibration result?" -msgstr "" +msgstr "Come utilizzare il risultato della calibrazione?" msgid "" "You could change the Flow Dynamics Calibration Factor in material editing" msgstr "" +"È possibile modificare il fattore di calibrazione della dinamica del flusso " +"nella modifica del materiale" msgid "" "The current firmware version of the printer does not support calibration.\n" "Please upgrade the printer firmware." msgstr "" +"La versione corrente del firmware della stampante non supporta la " +"calibrazione.\n" +"Aggiornare il firmware della stampante." msgid "Calibration not supported" -msgstr "" +msgstr "Calibrazione non supportata" msgid "Flow Dynamics" -msgstr "" +msgstr "Dinamica del flusso" msgid "Flow Rate" -msgstr "" +msgstr "Flusso" msgid "Max Volumetric Speed" -msgstr "" +msgstr "Massima velocità volumetrica" msgid "Please enter the name you want to save to printer." -msgstr "" +msgstr "Immettere il nome che si desidera salvare nella stampante." msgid "The name cannot exceed 40 characters." -msgstr "" +msgstr "Il nome non può superare i 40 caratteri." #, c-format, boost-format msgid "" @@ -10945,62 +11602,75 @@ msgid "" "End value: > Start value\n" "Value step: >= %.3f)" msgstr "" +"Immettere valori validi:\n" +"Valore iniziale: >= %.1f\n" +"Valore finale: <= %.1f\n" +"Valore finale: > Valore iniziale\n" +"Passo valore: >= %.3f)" msgid "The name cannot be empty." -msgstr "" +msgstr "Il nome non può essere vuoto." #, boost-format msgid "The selected preset: %1% is not found." -msgstr "" +msgstr "Il preset selezionato: %1% non è stato trovato." msgid "The name cannot be the same as the system preset name." msgstr "" +"Il nome non può essere uguale al nome della preimpostazione di sistema." msgid "The name is the same as another existing preset name" -msgstr "" +msgstr "Il nome è lo stesso di un altro nome predefinito esistente" msgid "create new preset failed." -msgstr "" +msgstr "La creazione di un nuovo predefinito non è riuscita." msgid "" "Are you sure to cancel the current calibration and return to the home page?" msgstr "" +"Sei sicuro di annullare la calibrazione corrente e tornare alla home page?" msgid "No Printer Connected!" -msgstr "" +msgstr "Nessuna stampante collegata!" msgid "Printer is not connected yet." -msgstr "" +msgstr "La stampante non è ancora connessa." msgid "Please select filament to calibrate." -msgstr "" +msgstr "Selezionare il filamento da calibrare." msgid "The input value size must be 3." -msgstr "" +msgstr "La dimensione del valore di input deve essere 3." msgid "Connecting to printer..." -msgstr "" +msgstr "Collegamento alla stampante..." msgid "The failed test result has been dropped." -msgstr "" +msgstr "Il risultato del test non riuscito è stato eliminato." msgid "Flow Dynamics Calibration result has been saved to the printer" msgstr "" +"Il risultato della calibrazione di Flow Dynamics è stato salvato nella " +"stampante" msgid "Internal Error" -msgstr "" +msgstr "Errore interno" msgid "Please select at least one filament for calibration" -msgstr "" +msgstr "Si prega di selezionare almeno un filamento per la calibrazione" msgid "Flow rate calibration result has been saved to preset" msgstr "" +"Il risultato della calibrazione della portata è stato salvato nella " +"preimpostazione" msgid "Max volumetric speed calibration result has been saved to preset" msgstr "" +"Il risultato della calibrazione della velocità volumetrica massima è stato " +"salvato in un valore preimpostato" msgid "When do you need Flow Dynamics Calibration" -msgstr "" +msgstr "Quando è necessaria la calibrazione della dinamica del flusso" msgid "" "We now have added the auto-calibration for different filaments, which is " @@ -11012,9 +11682,18 @@ msgid "" "3. If the max volumetric speed or print temperature is changed in the " "filament setting." msgstr "" +"Ora abbiamo aggiunto la calibrazione automatica per diversi filamenti, che è " +"completamente automatizzata e il risultato verrà salvato nella stampante per " +"un uso futuro. È necessario eseguire la calibrazione solo nei seguenti casi " +"limitati:\n" +"1. Se si introduce un nuovo filamento di marche/modelli diversi o il " +"filamento è umido;\n" +"2. se l'ugello è usurato o sostituito con uno nuovo;\n" +"3. Se la velocità volumetrica massima o la temperatura di stampa vengono " +"modificate nell'impostazione del filamento." msgid "About this calibration" -msgstr "" +msgstr "Informazioni su questa calibrazione" msgid "" "Please find the details of Flow Dynamics Calibration from our wiki.\n" @@ -11035,9 +11714,30 @@ msgid "" "cause the result not exactly the same in each calibration. We are still " "investigating the root cause to do improvements with new updates." msgstr "" +"I dettagli della calibrazione dinamica del flusso sono disponibili nel " +"nostro wiki.\n" +"\n" +"Di solito la calibrazione non è necessaria. Quando si avvia una stampa a " +"singolo colore/materiale, con l'opzione \"calibrazione dinamica del flusso\" " +"selezionata nel menu di avvio della stampa, la stampante seguirà il vecchio " +"modo, calibrando il filamento prima della stampa; Quando si avvia una stampa " +"multicolore/materiale, la stampante utilizzerà il parametro di compensazione " +"predefinito per il filamento durante ogni cambio di filamento, che avrà un " +"buon risultato nella maggior parte dei casi.\n" +"\n" +"Si prega di notare che ci sono alcuni casi che renderanno il risultato della " +"calibrazione non affidabile: utilizzo di una piastra di texture per eseguire " +"la calibrazione; La piastra di costruzione non ha una buona adesione (si " +"prega di lavare la piastra di costruzione o applicare la colla stick!) ... " +"Puoi trovare di più dal nostro wiki.\n" +"\n" +"I risultati della calibrazione hanno un jitter di circa il 10% nel nostro " +"test, il che potrebbe causare un risultato non esattamente lo stesso in ogni " +"calibrazione. Stiamo ancora indagando sulla causa principale per apportare " +"miglioramenti con i nuovi aggiornamenti." msgid "When to use Flow Rate Calibration" -msgstr "" +msgstr "Quando utilizzare la calibrazione della portata" msgid "" "After using Flow Dynamics Calibration, there might still be some extrusion " @@ -11050,12 +11750,28 @@ msgid "" "4. Weak Structural Integrity: Prints break easily or don't seem as sturdy as " "they should be." msgstr "" +"Dopo aver utilizzato la calibrazione di Flow Dynamics, potrebbero esserci " +"ancora alcuni problemi di estrusione, ad esempio:\n" +"1. Sovraestrusione: materiale in eccesso sull'oggetto stampato, formazione " +"di bolle o brufoli o strati che sembrano più spessi del previsto e non " +"uniformi.\n" +"2. Sottoestrusione: strati molto sottili, resistenza al riempimento debole o " +"spazi vuoti nello strato superiore del modello, anche quando si stampa " +"lentamente.\n" +"3. Scarsa qualità della superficie: la superficie delle stampe sembra ruvida " +"o irregolare.\n" +"4. Integrità strutturale debole: le stampe si rompono facilmente o non " +"sembrano robuste come dovrebbero essere." msgid "" "In addition, Flow Rate Calibration is crucial for foaming materials like LW-" "PLA used in RC planes. These materials expand greatly when heated, and " "calibration provides a useful reference flow rate." msgstr "" +"Inoltre, la calibrazione della portata è fondamentale per i materiali " +"schiumogeni come l'LW-PLA utilizzato negli aerei RC. Questi materiali si " +"espandono notevolmente quando vengono riscaldati e la calibrazione fornisce " +"un'utile portata di riferimento." msgid "" "Flow Rate Calibration measures the ratio of expected to actual extrusion " @@ -11065,6 +11781,13 @@ msgid "" "you still see the listed defects after you have done other calibrations. For " "more details, please check out the wiki article." msgstr "" +"La calibrazione della portata misura il rapporto tra i volumi di estrusione " +"previsti e quelli effettivi. L'impostazione predefinita funziona bene nelle " +"stampanti Bambu Lab e nei filamenti ufficiali in quanto sono stati pre-" +"calibrati e messi a punto. Per un filamento normale, di solito non è " +"necessario eseguire una calibrazione della portata, a meno che non si vedano " +"ancora i difetti elencati dopo aver eseguito altre calibrazioni. Per " +"maggiori dettagli, consulta l'articolo wiki." msgid "" "Auto Flow Rate Calibration utilizes Bambu Lab's Micro-Lidar technology, " @@ -11084,34 +11807,58 @@ msgid "" "can lead to sub-par prints or printer damage. Please make sure to carefully " "read and understand the process before doing it." msgstr "" +"La calibrazione automatica della portata utilizza la tecnologia Micro-Lidar " +"di Bambu Lab, misurando direttamente i modelli di calibrazione. Tuttavia, si " +"prega di notare che l'efficacia e l'accuratezza di questo metodo possono " +"essere compromesse con tipi specifici di materiali. In particolare, i " +"filamenti trasparenti o semitrasparenti, con particelle scintillanti o con " +"una finitura altamente riflettente potrebbero non essere adatti a questa " +"calibrazione e possono produrre risultati tutt'altro che desiderabili.\n" +"\n" +"I risultati della calibrazione possono variare a seconda della calibrazione " +"o del filamento. Stiamo ancora migliorando l'accuratezza e la compatibilità " +"di questa calibrazione attraverso aggiornamenti del firmware nel tempo.\n" +"\n" +"Attenzione: La taratura della portata è un processo avanzato, che può essere " +"tentato solo da coloro che ne comprendono appieno lo scopo e le " +"implicazioni. Un uso errato può causare stampe scadenti o danni alla " +"stampante. Assicurati di leggere attentamente e comprendere il processo " +"prima di farlo." msgid "When you need Max Volumetric Speed Calibration" -msgstr "" +msgstr "Quando è necessaria la calibrazione della velocità volumetrica massima" msgid "Over-extrusion or under extrusion" -msgstr "" +msgstr "Sovraestrusione o sottoestrusione" msgid "Max Volumetric Speed calibration is recommended when you print with:" msgstr "" +"La calibrazione della velocità volumetrica massima è consigliata quando si " +"stampa con:" msgid "material with significant thermal shrinkage/expansion, such as..." -msgstr "" +msgstr "materiale con notevole ritiro/dilatazione termica, come..." msgid "materials with inaccurate filament diameter" -msgstr "" +msgstr "materiali con diametro del filamento impreciso" msgid "We found the best Flow Dynamics Calibration Factor" msgstr "" +"Abbiamo trovato il miglior fattore di calibrazione della dinamica del flusso" msgid "" "Part of the calibration failed! You may clean the plate and retry. The " "failed test result would be dropped." msgstr "" +"Parte della calibrazione non è riuscita! È possibile pulire la piastra e " +"riprovare. Il risultato del test non riuscito verrebbe eliminato." msgid "" "*We recommend you to add brand, materia, type, and even humidity level in " "the Name" msgstr "" +"*Ti consigliamo di aggiungere marca, materia, tipo e persino livello di " +"umidità nel Nome" msgid "Failed" msgstr "Fallito" @@ -11120,6 +11867,8 @@ msgid "" "Only one of the results with the same name will be saved. Are you sure you " "want to overrides the other results?" msgstr "" +"Verrà salvato solo uno dei risultati con lo stesso nome. Si è sicuri di " +"voler sovrascrivere gli altri risultati?" #, c-format, boost-format msgid "" @@ -11127,330 +11876,346 @@ msgid "" "Only one of the results with the same name is saved. Are you sure you want " "to overrides the historical result?" msgstr "" +"Esiste già un risultato di calibrazione storico con lo stesso nome: %s. " +"Viene salvato solo uno dei risultati con lo stesso nome. Si è sicuri di " +"voler eseguire l'override del risultato cronologico?" msgid "Please find the best line on your plate" -msgstr "" +msgstr "Trova la linea migliore nel tuo piatto" msgid "Please find the cornor with perfect degree of extrusion" -msgstr "" +msgstr "Si prega di trovare il cornor con il perfetto grado di estrusione" msgid "Input Value" -msgstr "" +msgstr "Valore di input" msgid "Save to Filament Preset" -msgstr "" +msgstr "Salva nel preset del filamento" msgid "Preset" -msgstr "" +msgstr "Preselezionato" msgid "Record Factor" -msgstr "" +msgstr "Fattore record" msgid "We found the best flow ratio for you" -msgstr "" +msgstr "Abbiamo trovato il miglior rapporto di flusso per te" msgid "Flow Ratio" -msgstr "" +msgstr "Rapporto di flusso" msgid "Please input a valid value (0.0 < flow ratio < 2.0)" -msgstr "" +msgstr "Immettere un valore valido (rapporto di flusso 0,0 < < 2,0)" msgid "Please enter the name of the preset you want to save." -msgstr "" +msgstr "Immettere il nome del preset che si desidera salvare." msgid "Calibration1" -msgstr "" +msgstr "Calibrazione1" msgid "Calibration2" -msgstr "" +msgstr "Calibrazione2" msgid "Please find the best object on your plate" -msgstr "" +msgstr "Trova l'oggetto migliore nel tuo piatto" msgid "Fill in the value above the block with smoothest top surface" msgstr "" +"Riempi il valore sopra il blocco con la superficie superiore più liscia" msgid "Skip Calibration2" -msgstr "" +msgstr "Salta calibrazione2" #, c-format, boost-format msgid "flow ratio : %s " -msgstr "" +msgstr "Rapporto di flusso : %s " msgid "Please choose a block with smoothest top surface" -msgstr "" +msgstr "Si prega di scegliere un blocco con la superficie superiore più liscia" msgid "Please choose a block with smoothest top surface." msgstr "" +"Si prega di scegliere un blocco con la superficie superiore più liscia." msgid "Please input a valid value (0 <= Max Volumetric Speed <= 60)" -msgstr "" +msgstr "Inserire un valore valido (0 <= Velocità volumetrica massima <= 60)" msgid "Calibration Type" -msgstr "" +msgstr "Tipo di calibrazione" msgid "Complete Calibration" -msgstr "" +msgstr "Calibrazione completa" msgid "Fine Calibration based on flow ratio" -msgstr "" +msgstr "Calibrazione fine in base al rapporto di flusso" msgid "Title" -msgstr "" +msgstr "Titolo" msgid "" "A test model will be printed. Please clear the build plate and place it back " "to the hot bed before calibration." msgstr "" +"Verrà stampato un modello di prova. Si prega di pulire la piastra di " +"costruzione e riposizionarla sul letto caldo prima della calibrazione." msgid "Printing Parameters" -msgstr "" +msgstr "Parametri di stampa" msgid "- ℃" -msgstr "" +msgstr "- °C" msgid " ℃" -msgstr "" +msgstr " °C" msgid "Plate Type" msgstr "Tipo di piatto" msgid "filament position" -msgstr "" +msgstr "Posizione del filamento" msgid "External Spool" -msgstr "" +msgstr "Bobina esterna" msgid "Filament For Calibration" -msgstr "" +msgstr "Filamento per calibrazione" msgid "" "Tips for calibration material: \n" "- Materials that can share same hot bed temperature\n" "- Different filament brand and family(Brand = Bambu, Family = Basic, Matte)" msgstr "" +"Suggerimenti per il materiale di calibrazione: \n" +"- Materiali che possono condividere la stessa temperatura del letto caldo\n" +"- Diverse marche e famiglie di filamenti (Marca = Bambu, Famiglia = Basic, " +"Matte)" msgid "Error desc" -msgstr "" +msgstr "Errore desc" msgid "Extra info" -msgstr "" +msgstr "Ulteriori informazioni" msgid "Pattern" -msgstr "" +msgstr "Trama" msgid "Method" -msgstr "" +msgstr "Metodo" #, c-format, boost-format msgid "%s is not compatible with %s" -msgstr "" +msgstr "%s non è compatibile con %s" msgid "TPU is not supported for Flow Dynamics Auto-Calibration." msgstr "" +"Il TPU non è supportato per la calibrazione automatica di Flow Dynamics." msgid "Connecting to printer" -msgstr "" +msgstr "Collegamento alla stampante" msgid "From k Value" -msgstr "" +msgstr "Da k Valore" msgid "To k Value" -msgstr "" +msgstr "Al valore k" msgid "Step value" -msgstr "" +msgstr "Valore del passaggio" msgid "0.5" -msgstr "" +msgstr "0.5" msgid "0.005" -msgstr "" +msgstr "0.005" msgid "The nozzle diameter has been synchronized from the printer Settings" msgstr "" +"Il diametro dell'ugello è stato sincronizzato dalle impostazioni della " +"stampante" msgid "From Volumetric Speed" -msgstr "" +msgstr "Dalla velocità volumetrica" msgid "To Volumetric Speed" -msgstr "" +msgstr "Velocità volumetrica" msgid "Flow Dynamics Calibration Result" -msgstr "" +msgstr "Risultato della calibrazione della dinamica del flusso" msgid "No History Result" -msgstr "" +msgstr "Nessun risultato della cronologia" msgid "Success to get history result" -msgstr "" +msgstr "Successo per ottenere il risultato della cronologia" msgid "Refreshing the historical Flow Dynamics Calibration records" -msgstr "" +msgstr "Aggiornamento dei record storici di calibrazione di Flow Dynamics" msgid "Action" -msgstr "" +msgstr "Azione" msgid "Edit Flow Dynamics Calibration" -msgstr "" +msgstr "Modifica calibrazione dinamica flusso (Edit Flow Dynamics)" msgid "Network lookup" -msgstr "" +msgstr "Ricerca network" msgid "Address" -msgstr "" +msgstr "Indirizzo" msgid "Hostname" -msgstr "" +msgstr "Nome Host" msgid "Service name" -msgstr "" +msgstr "Nome servizio" msgid "OctoPrint version" -msgstr "" +msgstr "Versione OctoPrint" msgid "Searching for devices" -msgstr "" +msgstr "Ricerca dispositivi" msgid "Finished" msgstr "Finito" msgid "Multiple resolved IP addresses" -msgstr "" +msgstr "Indirizzi IP multipli risolti" #, boost-format msgid "" "There are several IP addresses resolving to hostname %1%.\n" "Please select one that should be used." msgstr "" +"Esistono diversi indirizzi IP che risolvono il nome host %1%.\n" +"Selezionare quello da utilizzare." msgid "Unable to perform boolean operation on selected parts" -msgstr "" +msgstr "Impossibile eseguire un'operazione booleana sulle parti selezionate" msgid "Mesh Boolean" -msgstr "" +msgstr "Mesh booleano" msgid "Union" -msgstr "" +msgstr "Unione" msgid "Difference" -msgstr "" +msgstr "Differenza" msgid "Intersection" -msgstr "" +msgstr "Intersezione" msgid "Source Volume" -msgstr "" +msgstr "Volume sorgente" msgid "Tool Volume" -msgstr "" +msgstr "Volume dell'utensile" msgid "Subtract from" -msgstr "" +msgstr "Sottrai da" msgid "Subtract with" -msgstr "" +msgstr "Sottrai" msgid "selected" -msgstr "" +msgstr "selezionato" msgid "Part 1" -msgstr "" +msgstr "Partita #1" msgid "Part 2" -msgstr "" +msgstr "Partita #2" msgid "Delete input" -msgstr "" +msgstr "Eliminare l'input" msgid "Send G-Code to printer host" -msgstr "" +msgstr "Invia G-code all’host stampante" msgid "Upload to Printer Host with the following filename:" -msgstr "" +msgstr "Carica all'Host di stampa con il seguente nome file:" msgid "Use forward slashes ( / ) as a directory separator if needed." -msgstr "" +msgstr "Usa la barra ( / ) come separatore di cartella se necessario." msgid "Upload to storage" -msgstr "" +msgstr "Carica nello spazio di archiviazione" #, c-format, boost-format msgid "Upload filename doesn't end with \"%s\". Do you wish to continue?" -msgstr "" +msgstr "Il nome del file caricato non finisce con \"%s\". Vuoi continuare?" msgid "Upload" -msgstr "" +msgstr "Carica" msgid "Print host upload queue" -msgstr "" +msgstr "Coda di caricamento Host di stampa" msgid "ID" -msgstr "" +msgstr "ID" msgid "Progress" -msgstr "" +msgstr "Progresso" msgid "Host" -msgstr "" +msgstr "Host" msgctxt "OfFile" msgid "Size" -msgstr "" +msgstr "Dimensione" msgid "Filename" -msgstr "" +msgstr "Nome file" msgid "Cancel selected" -msgstr "" +msgstr "Cancella selezione" msgid "Show error message" -msgstr "" +msgstr "Mostra messaggio d'errore" msgid "Enqueued" -msgstr "" +msgstr "Messo in coda" msgid "Uploading" msgstr "Caricamento" msgid "Cancelling" -msgstr "" +msgstr "Annullamento" msgid "Error uploading to print host" -msgstr "" +msgstr "Errore durante il caricamento dell'host di stampa" msgid "PA Calibration" -msgstr "" +msgstr "Calibrazione PA" msgid "DDE" -msgstr "" +msgstr "Richiesta DDE (poke) fallita" msgid "Bowden" -msgstr "" +msgstr "Bowden" msgid "Extruder type" -msgstr "" +msgstr "Tipo di estrusore" msgid "PA Tower" -msgstr "" +msgstr "Torre PA" msgid "PA Line" -msgstr "" +msgstr "Linea PA" msgid "PA Pattern" -msgstr "" +msgstr "Modello PA" msgid "Start PA: " -msgstr "" +msgstr "Avvia PA: " msgid "End PA: " -msgstr "" +msgstr "Fine PA: " msgid "PA step: " -msgstr "" +msgstr "Passo PA: " msgid "Print numbers" -msgstr "" +msgstr "Numeri di stampa" msgid "" "Please input valid values:\n" @@ -11458,39 +12223,43 @@ msgid "" "End PA: > Start PA\n" "PA step: >= 0.001)" msgstr "" +"Immettere valori validi:\n" +"Avvia PA: >= 0.0\n" +"Fine PA: > Avvia PA\n" +"Passo PA: >= 0,001)" msgid "Temperature calibration" -msgstr "" +msgstr "Calibrazione della temperatura" msgid "PLA" -msgstr "" +msgstr "PLA" msgid "ABS/ASA" -msgstr "" +msgstr "ABS/ASA" msgid "PETG" -msgstr "" +msgstr "PETG" msgid "TPU" -msgstr "" +msgstr "TPU (TPU)" msgid "PA-CF" -msgstr "" +msgstr "PA-CF" msgid "PET-CF" -msgstr "" +msgstr "PET-CF" msgid "Filament type" -msgstr "" +msgstr "Tipo filamento" msgid "Start temp: " -msgstr "" +msgstr "Temperatura di avvio: " msgid "End end: " -msgstr "" +msgstr "Fine fine: " msgid "Temp step: " -msgstr "" +msgstr "Fase di temperatura: " msgid "" "Please input valid values:\n" @@ -11498,18 +12267,22 @@ msgid "" "End temp: >= 170\n" "Start temp > End temp + 5)" msgstr "" +"Immettere valori validi:\n" +"Temperatura iniziale: <= 350\n" +"Temperatura finale: >= 170\n" +"Temperatura di inizio > Temperatura di fine + 5)" msgid "Max volumetric speed test" -msgstr "" +msgstr "Test di velocità volumetrica massima" msgid "Start volumetric speed: " -msgstr "" +msgstr "Velocità volumetrica iniziale: " msgid "End volumetric speed: " -msgstr "" +msgstr "Velocità volumetrica finale: " msgid "step: " -msgstr "" +msgstr "Step: " msgid "" "Please input valid values:\n" @@ -11517,15 +12290,19 @@ msgid "" "step >= 0\n" "end > start + step)" msgstr "" +"Immettere valori validi:\n" +"inizio > 0 \n" +"passo >= 0\n" +"fine > inizio + passo)" msgid "VFA test" -msgstr "" +msgstr "Prova VFA" msgid "Start speed: " -msgstr "" +msgstr "Velocità di avvio: " msgid "End speed: " -msgstr "" +msgstr "Velocità finale: " msgid "" "Please input valid values:\n" @@ -11533,69 +12310,82 @@ msgid "" "step >= 0\n" "end > start + step)" msgstr "" +"Immettere valori validi:\n" +"Inizio > 10 \n" +"passo >= 0\n" +"fine > inizio + passo)" msgid "Start retraction length: " -msgstr "" +msgstr "Lunghezza di retrazione iniziale: " msgid "End retraction length: " -msgstr "" +msgstr "Lunghezza di retrazione finale: " msgid "mm/mm" -msgstr "" +msgstr "mm/mm" msgid "Physical Printer" -msgstr "" +msgstr "Stampante Fisica" msgid "Print Host upload" -msgstr "" +msgstr "Caricamento Host di stampa" msgid "Test" -msgstr "" +msgstr "Test" msgid "Could not get a valid Printer Host reference" -msgstr "" +msgstr "Impossibile ottenere un riferimento Host Stampante valido" msgid "Success!" -msgstr "" +msgstr "Successo!" msgid "Refresh Printers" -msgstr "" +msgstr "Aggiorna Stampanti" msgid "" "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" "signed certificate." msgstr "" +"File HTTPS CA opzionale. È necessario solo se si intende usare un HTTPS con " +"certificato autofirmato." msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" -msgstr "" +msgstr "File di certificato (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgid "Open CA certificate file" -msgstr "" +msgstr "Apri file di certificato CA" #, c-format, boost-format msgid "" "On this system, %s uses HTTPS certificates from the system Certificate Store " "or Keychain." msgstr "" +"Su questo sistema, %s utilizza certificati HTTPS provenienti dal sistema " +"Certificate Store o da Keychain." msgid "" "To use a custom CA file, please import your CA file into Certificate Store / " "Keychain." msgstr "" +"Per utilizzare un file CA personalizzato, importa il tuo file CA sul " +"Certificate Store / Keychain." msgid "Connection to printers connected via the print host failed." msgstr "" +"Collegamento alle stampanti collegate tramite l'host di stampa fallito." msgid "The start, end or step is not valid value." -msgstr "" +msgstr "L'inizio, la fine o il passo non sono valori validi." msgid "" "Unable to calibrate: maybe because the set calibration value range is too " "large, or the step is too small" msgstr "" +"Impossibile calibrare: forse perché l'intervallo di valori di calibrazione " +"impostato è troppo ampio o il passo è troppo piccolo" msgid "Need select printer" -msgstr "" +msgstr "Hai bisogno di selezionare la stampante" #: resources/data/hints.ini: [hint:3D Scene Operations] msgid "" @@ -11715,6 +12505,11 @@ msgid "" "part modifier? That way you can, for example, create easily resizable holes " "directly in Orca Slicer. Read more in the documentation." msgstr "" +"Sottrazione di una parte\n" +"Sapevi che puoi sottrarre una mesh da un'altra usando il modificatore Parte " +"negativa? In questo modo è possibile, ad esempio, creare fori facilmente " +"ridimensionabili direttamente in Orca Slicer. Per ulteriori informazioni, " +"consulta la documentazione." #: resources/data/hints.ini: [hint:STEP] msgid "" @@ -11724,6 +12519,11 @@ msgid "" "Orca Slicer supports slicing STEP files, providing smoother results than a " "lower resolution STL. Give it a try!" msgstr "" +"PASSO\n" +"Sapevi che puoi migliorare la tua qualità di stampa tagliando un file STEP " +"invece di un STL?\n" +"Orca Slicer supporta il sezionamento dei file STEP, fornendo risultati più " +"uniformi rispetto a un STL a risoluzione inferiore. Provateci!" #: resources/data/hints.ini: [hint:Z seam location] msgid "" @@ -11864,16 +12664,21 @@ msgid "" "clogging when printing lower temperature filament with a higher enclosure " "temperature. More info about this in the Wiki." msgstr "" +"Quando è necessario stampare con lo sportello della stampante aperto\n" +"L'apertura dello sportello della stampante può ridurre la probabilità di " +"intasamento dell'estrusore/hotend quando si stampa un filamento a " +"temperatura inferiore con una temperatura dell'involucro più elevata. " +"Maggiori informazioni su questo nel Wiki." #~ msgid "Embeded" #~ msgstr "Integrato" -#~ msgid "Online Models" -#~ msgstr "Modelli Online" - #~ msgid "Show online staff-picked models on the home page" #~ msgstr "Mostra i modelli online selezionati dallo staff nella home page" +#~ msgid "Online Models" +#~ msgstr "Modelli Online" + #~ msgid "The minimum printing speed when slow down for cooling" #~ msgstr "The minimum printing speed when slowing down for cooling." diff --git a/localization/i18n/ko/OrcaSlicer_ko.po b/localization/i18n/ko/OrcaSlicer_ko.po index 959e04a202..ae654dc978 100644 --- a/localization/i18n/ko/OrcaSlicer_ko.po +++ b/localization/i18n/ko/OrcaSlicer_ko.po @@ -8,8 +8,9 @@ msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-11-10 14:54+0800\n" -"PO-Revision-Date: 2023-10-26 16:47+0900\n" -"Last-Translator: Hotsolidinfill\n" +"PO-Revision-Date: 2023-11-19 11:26+0900\n" +"Last-Translator: Hotsolidinfill <138652683+Hotsolidinfill@users.noreply." +"github.com>, crwusiz \n" "Language-Team: \n" "Language: ko_KR\n" "MIME-Version: 1.0\n" @@ -653,6 +654,9 @@ msgid "" "Please note, application settings will be lost, but printer profiles will " "not be affected." msgstr "" +"OrcaSlicer 구성 파일이 손상되어 구문을 분석할 수 없습니다.\n" +"OrcaSlicer가 구성 파일을 다시 생성하려고 시도했습니다.\n" +"응용 프로그램 설정은 손실되지만 프린터 프로필은 영향을 받지 않습니다." msgid "Rebuild" msgstr "재빌드" @@ -853,16 +857,16 @@ msgid "Load..." msgstr "불러오기..." msgid "Orca Cube" -msgstr "Orca Cube" +msgstr "Orca 큐브" msgid "3DBenchy" -msgstr "3DBenchy" +msgstr "3D 벤치" msgid "Autodesk FDM Test" -msgstr "Autodesk FDM Test" +msgstr "Autodesk FDM 테스트" msgid "Voron Cube" -msgstr "Voron Cube" +msgstr "Voron 큐브" msgid "Cube" msgstr "정육면체" @@ -1351,7 +1355,7 @@ msgid "Infill density(%)" msgstr "내부 채움 밀도(%)" msgid "Auto Brim" -msgstr "자동 챙(브림)" +msgstr "자동 브림" msgid "Auto" msgstr "자동" @@ -1360,16 +1364,16 @@ msgid "Mouse ear" msgstr "생쥐 귀" msgid "Outer brim only" -msgstr "외부 챙만" +msgstr "외부 브림만" msgid "Inner brim only" -msgstr "내부 챙만" +msgstr "내부 브림만" msgid "Outer and inner brim" -msgstr "내부와 외부 챙" +msgstr "내부와 외부 브림" msgid "No-brim" -msgstr "챙 비활성화" +msgstr "브림 비활성화" msgid "Outer wall speed" msgstr "외벽 속도" @@ -1378,7 +1382,7 @@ msgid "Plate" msgstr "플레이트" msgid "Brim" -msgstr "챙(브림)" +msgstr "브림" msgid "Object/Part Setting" msgstr "개체/부품 설정" @@ -1948,10 +1952,10 @@ msgid "PA Profile" msgstr "PA 프로필" msgid "Factor K" -msgstr "Factor K" +msgstr "K 계수" msgid "Factor N" -msgstr "Factor N" +msgstr "N 계수" msgid "Setting Virtual slot information while printing is not supported" msgstr "출력 중에는 가상 슬롯 정보 설정이 지원되지 않습니다" @@ -2021,7 +2025,7 @@ msgid "" "factor K input box." msgstr "" "교정이 완료되었습니다. 당신의 고온 베드에서 아래 사진과 같이 가장 균일한 압" -"출 선을 찾아 왼쪽에 있는 값을 입력 상자의 Factor K에 채워주세요." +"출 선을 찾아 왼쪽에 있는 값을 입력 상자의 K 계수에 채워주세요." msgid "Save" msgstr "저장" @@ -2545,7 +2549,7 @@ msgid "Auto bed leveling" msgstr "자동 베드 레벨링" msgid "Heatbed preheating" -msgstr "고온 베드 예열" +msgstr "베드 예열" msgid "Sweeping XY mech mode" msgstr "스위핑 XY 기계 모드" @@ -2608,7 +2612,7 @@ msgid "Filament unloading" msgstr "필라멘트 빼는 중" msgid "Skip step pause" -msgstr "단계 일시정지 건너뛰기" +msgstr "단계 건너뛰기 일시중지" msgid "Filament loading" msgstr "필라멘트 넣는 중" @@ -2692,7 +2696,7 @@ msgid "" "automatically be set to 0℃." msgstr "" "챔버 온도를 40℃ 이하로 설정하면 챔버 온도 제어가 활성화되지 않습니다. 그리고 " -"목표 챔버 온도는 자동으로 0℃로 설정됩니다." +"목표 챔버 온도는 자동으로 0℃ 로 설정됩니다." msgid "Failed to start printing job" msgstr "출력 작업을 시작하지 못했습니다" @@ -2894,7 +2898,7 @@ msgid "Retract" msgstr "퇴출" msgid "Unretract" -msgstr "비퇴출(언리트렉트)" +msgstr "비퇴출" msgid "Filament Changes" msgstr "필라멘트 변경" @@ -3041,7 +3045,7 @@ msgid "Avoid extrusion calibration region" msgstr "압출 교정 영역을 피하십시오" msgid "Align to Y axis" -msgstr "Y축으로 정렬" +msgstr "Y축에 정렬" msgid "Add" msgstr "추가" @@ -3241,7 +3245,7 @@ msgid "will be closed before creating a new model. Do you want to continue?" msgstr "새 모델을 생성하기 전에 닫힙니다. 계속하시겠습니까?" msgid "Slice plate" -msgstr "슬라이스 플레이트" +msgstr "플레이트 슬라이스" msgid "Print plate" msgstr "플레이트 출력" @@ -3388,7 +3392,7 @@ msgid "Export all objects as STL" msgstr "모든 개체를 STL로 내보내기" msgid "Export Generic 3MF" -msgstr "Generic 3MF 내보내기" +msgstr "일반 3MF 내보내기" msgid "Export 3mf file without using some 3mf-extensions" msgstr "3mf-extensions을 사용하지 않고 3mf 파일 내보내기" @@ -3481,10 +3485,10 @@ msgid "Show object labels in 3D scene" msgstr "3D 화면에 개체 이름표 표시" msgid "Show &Overhang" -msgstr "돌출부 &보기" +msgstr "돌출부 보기" msgid "Show object overhang highlight in 3D scene" -msgstr "3D 장면에서 객체 오버행 하이라이트 표시" +msgstr "3D 장면에서 개체 오버행 하이라이트 표시" msgid "Preferences" msgstr "기본 설정" @@ -4014,7 +4018,7 @@ msgid "Silent" msgstr "조용한" msgid "Standard" -msgstr "스탠다드" +msgstr "표준" msgid "Sport" msgstr "스포츠" @@ -4506,7 +4510,7 @@ msgid "" "clogged when printing this filament in a closed enclosure. Please open the " "front door and/or remove the upper glass." msgstr "" -"현재의 고온 베드 온도가 상대적으로 높습니다. 닫힌 공간에서 이 필라멘트를 출력" +"현재 베드 온도가 상대적으로 높습니다. 닫힌 공간에서 이 필라멘트를 출력" "할 때 노즐이 막힐 수 있습니다. 전면 도어를 열거나 상단 유리를 제거하세요." msgid "" @@ -4686,13 +4690,13 @@ msgid "Message" msgstr "메시지" msgid "Reload from:" -msgstr "다음에서 다시 로드:" +msgstr "다음에서 새로고침:" msgid "Unable to reload:" -msgstr "다시 로드할 수 없음:" +msgstr "새로고침할 수 없음:" msgid "Error during reload" -msgstr "다시 로드 중 오류 발생" +msgstr "새로고침 중 오류가 발생했습니다." msgid "Slicing" msgstr "슬라이싱" @@ -4998,13 +5002,13 @@ msgid "Units" msgstr "단위" msgid "Home" -msgstr "" +msgstr "홈" msgid "Default Page" -msgstr "" +msgstr "기본 페이지" msgid "Set the page opened on startup." -msgstr "" +msgstr "시작 시 열리는 페이지를 설정합니다." msgid "Zoom to mouse position" msgstr "마우스 위치로 확대" @@ -5023,10 +5027,10 @@ msgstr "" "카메라 앵글을 사용합니다." msgid "Show splash screen" -msgstr "" +msgstr "스플래시 화면 표시" msgid "Show the splash screen during startup." -msgstr "" +msgstr "시작하는 동안 스플래시 화면을 표시합니다." msgid "Show \"Tip of the day\" notification after start" msgstr "시작 후 \"오늘의 팁\" 알림 표시" @@ -5616,7 +5620,7 @@ msgid "" "Caution to use! Flow calibration on Textured PEI Plate may fail due to the " "scattered surface." msgstr "" -"사용상의 주의! Textured PEI 플레이트의 유량 교정은 표면이 분산되어 실패할 수 " +"사용상의 주의! 텍스처 PEI 플레이트의 유량 교정은 표면이 분산되어 실패할 수 " "있습니다." msgid "Automatic flow calibration using Micro Lidar" @@ -5819,8 +5823,8 @@ msgstr "" 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\"." +"by right-click the empty position of build plate and choose \"Add " +"Primitive\"->\"Timelapse Wipe Tower\"." msgstr "" "툴헤드 없이 시간 경과를 기록할 경우 \"타임랩스 닦기 타워\"를 추가하는 것이 좋" "습니다\n" @@ -5870,7 +5874,7 @@ msgid "Set speed for external and internal bridges" msgstr "외부 및 내부 다리 속도 설정" msgid "Travel speed" -msgstr "이동 속도(트레블)" +msgstr "이동 속도" msgid "Acceleration" msgstr "가속도" @@ -5923,7 +5927,7 @@ msgid "Reserved keywords found" msgstr "예약어를 찾았습니다" msgid "Setting Overrides" -msgstr "설정 재정의(덮어쓰기)" +msgstr "설정 덮어쓰기" msgid "Retraction" msgstr "퇴출" @@ -6094,7 +6098,7 @@ msgid "Change filament G-code" msgstr "필라멘트 교체 G코드" msgid "Change extrusion role G-code" -msgstr "" +msgstr "압출 역할 G코드 변경" msgid "Pause G-code" msgstr "일시정지 G코드" @@ -6720,7 +6724,7 @@ msgid "A new Network plug-in(%s) available, Do you want to install it?" msgstr "새 네트워크 플러그인(%s)을 사용할 수 있습니다. 설치하시겠습니까?" msgid "New version of Orca Slicer" -msgstr "뱀부 스튜디오의 새 버전" +msgstr "Orca Slicer의 새 버전" msgid "Don't remind me of this version again" msgstr "이 버전을 다시 알리지 않음" @@ -6920,13 +6924,13 @@ msgid "Outer wall" msgstr "외벽" msgid "Overhang wall" -msgstr "돌출벽(오버행)" +msgstr "돌출벽" msgid "Sparse infill" msgstr "드문 내부 채움" msgid "Internal solid infill" -msgstr "내부 꽉찬 내부 채움" +msgstr "꽉찬 내부 채움" msgid "Top surface" msgstr "상단 표면" @@ -6968,7 +6972,7 @@ msgid "undefined error" msgstr "정의되지 않은 오류" msgid "too many files" -msgstr "너무 많은 파일" +msgstr "파일이 너무 많습니다" msgid "file too large" msgstr "파일이 너무 큽니다" @@ -6986,7 +6990,7 @@ msgid "failed finding central directory" msgstr "중앙 디렉토리를 찾지 못했습니다" msgid "not a ZIP archive" -msgstr "zip 아카이브가 아님" +msgstr "zip형식의 압축파일이 아님" msgid "invalid header or corrupted" msgstr "잘못된 헤더이거나 손상됨" @@ -7049,7 +7053,7 @@ msgid "file not found" msgstr "파일을 찾을 수 없습니다" msgid "archive too large" -msgstr "아카이브가 너무 큼" +msgstr "압축파일이 너무 큽니다" msgid "validation failed" msgstr "검증에 실패했습니다" @@ -7103,7 +7107,7 @@ msgid "" "Smooth mode of timelapse is not supported when \"by object\" sequence is " "enabled." msgstr "" -"타임랩스의 유연 모드는 \"개체별\" 출력순서가 ​​활성화된 경우 지원되지 않습니다." +"타임랩스의 유연 모드는 \"개체별\" 출력순서가 활성화된 경우 지원되지 않습니다." msgid "" "Please select \"By object\" print sequence to print multiple objects in " @@ -7234,7 +7238,7 @@ msgid "Plate %d: %s does not support filament %s" msgstr "%d: %s 플레이트는 %s 필라멘트를 지원하지 않습니다" msgid "Generating skirt & brim" -msgstr "스커트 & 챙(브림) 생성 중" +msgstr "스커트 & 브림 생성 중" msgid "Exporting G-code" msgstr "G코드 내보내는 중" @@ -7243,7 +7247,7 @@ msgid "Generating G-code" msgstr "G코드 생성 중" msgid "Failed processing of the filename_format template." -msgstr "파일 이름 형식(filename_format) 템플릿 처리에 실패했습니다." +msgstr "파일 이름 형식 템플릿 처리에 실패했습니다." msgid "Printable area" msgstr "출력 가능 영역" @@ -7390,7 +7394,7 @@ msgstr "벽 가로지름 방지" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" -"벽을 가로질러 이동하지 않고 우회하여 표면에 방울(Blob) 발생을 방지합니다" +"벽을 가로질러 이동하지 않고 우회하여 표면에 방울 발생을 방지합니다" msgid "Avoid crossing wall - Max detour length" msgstr "벽 가로지름 방지 - 최대 우회 길이" @@ -7446,7 +7450,7 @@ msgid "Initial layer" msgstr "초기 레이어" msgid "Initial layer bed temperature" -msgstr "초기 레이어 베드(Bed) 온도" +msgstr "초기 레이어 베드 온도" msgid "" "Bed temperature of the initial layer. Value 0 means the filament does not " @@ -7698,7 +7702,7 @@ msgid "Enable this option to slow printing down for different overhang degree" msgstr "돌출부 정도에 따라 출력 속도를 낮추려면 이 옵션을 활성화합니다" msgid "Slow down for curled perimeters" -msgstr "꺾여 있는 둘레에는 속도를 줄이세요" +msgstr "꺾여 있는 둘레에서 감속" msgid "" "Enable this option to slow printing down in areas where potential curled " @@ -7730,51 +7734,51 @@ msgstr "" "로 계산됩니다. 기본값은 150%입니다." msgid "Brim width" -msgstr "챙(브림) 너비" +msgstr "브림 너비" msgid "Distance from model to the outermost brim line" -msgstr "모델과 가장 바깥쪽 챙(브림) 선까지의 거리" +msgstr "모델과 가장 바깥쪽 브림 선까지의 거리" msgid "Brim type" -msgstr "챙(브림) 유형" +msgstr "브림 유형" msgid "" "This controls the generation of the brim at outer and/or inner side of " "models. Auto means the brim width is analysed and calculated automatically." msgstr "" -"모델의 외부 그리고/또는 내부에서 챙(브림)의 생성을 제어합니다. 자동은 챙(브" -"림) 너비가 자동으로 분석 및 계산됨을 의미합니다." +"모델의 외부 그리고/또는 내부에서 브림의 생성을 제어합니다. 자동은 브림" +"너비가 자동으로 분석 및 계산됨을 의미합니다." msgid "Brim-object gap" -msgstr "챙(브림)-개체 간격" +msgstr "브림-개체 간격" msgid "" "A gap between innermost brim line and object can make brim be removed more " "easily" msgstr "" -"가장 안쪽 챙(브림) 라인과 개체 사이에 간격을 주어 쉽게 챙(브림)을 제거 할 수 " +"가장 안쪽 브림 라인과 개체 사이에 간격을 주어 쉽게 브림을 제거 할 수 " "있게 합니다" msgid "Brim ears" -msgstr "챙(브림) 귀" +msgstr "브림 귀" msgid "Only draw brim over the sharp edges of the model." -msgstr "모델의 날카로운 가장자리에만 챙을 그립니다." +msgstr "모델의 날카로운 가장자리에만 브림을 그립니다." msgid "Brim ear max angle" -msgstr "챙(브림) 귀 최대 각도" +msgstr "브림 귀 최대 각도" msgid "" "Maximum angle to let a brim ear appear. \n" "If set to 0, no brim will be created. \n" "If set to ~180, brim will be created on everything but straight sections." msgstr "" -"챙 귀가 나타날 수 있는 최대 각도.\n" -"0으로 설정하면 챙이 생성되지 않습니다.\n" -"~180으로 설정하면 직선 부분을 제외한 모든 부분에 챙이 생성됩니다." +"브림 귀가 나타날 수 있는 최대 각도.\n" +"0으로 설정하면 브림이 생성되지 않습니다.\n" +"~180으로 설정하면 직선 부분을 제외한 모든 부분에 브림이 생성됩니다." msgid "Brim ear detection radius" -msgstr "챙 귀 감지 반경" +msgstr "브림 귀 감지 반경" msgid "" "The geometry will be decimated before dectecting sharp angles. This " @@ -7908,13 +7912,13 @@ msgstr "" "로 설정하고 다리에 지지대를 생성하지 않으려면 매우 큰 값으로 설정합니다." msgid "End G-code" -msgstr "End G-code" +msgstr "종료 G코드" msgid "End G-code when finish the whole printing" -msgstr "전체 출력이 끝날때의 End G-code" +msgstr "전체 출력이 끝날때의 종료 G코드" msgid "End G-code when finish the printing of this filament" -msgstr "이 필라멘트의 출력이 끝날때의 End G-code" +msgstr "이 필라멘트의 출력이 끝날때의 종료 G코드" msgid "Ensure vertical shell thickness" msgstr "수직 쉘 두께 확보" @@ -8331,7 +8335,7 @@ msgstr "가용성 재료" msgid "" "Soluble material is commonly used to print support and support interface" msgstr "" -"가용성 재료는 일반적으로 지지대 및 지지대 접점(Interface)을 출력하는 데 사용" +"가용성 재료는 일반적으로 지지대 및 지지대 접점을 출력하는 데 사용" "됩니다" msgid "Support material" @@ -8340,7 +8344,7 @@ msgstr "지지대 재료" msgid "" "Support material is commonly used to print support and support interface" msgstr "" -"지원 재료는 일반적으로 지지대 및 지지대 접점(Interface)을 출력하는 데 사용됩" +"지원 재료는 일반적으로 지지대 및 지지대 접점을 출력하는 데 사용됩" "니다" msgid "Softening temperature" @@ -8447,10 +8451,10 @@ msgstr "" "정합니다." msgid "0 (no open anchors)" -msgstr "0(개방형 고정점 없음)" +msgstr "0 (개방형 고정점 없음)" msgid "1000 (unlimited)" -msgstr "1000(무제한)" +msgstr "1000 (무제한)" msgid "Maximum length of the infill anchor" msgstr "내부 채움 고정점 최대 길이" @@ -8476,7 +8480,7 @@ msgstr "" "결과가 생성됩니다." msgid "0 (Simple connect)" -msgstr "0(단순 연결)" +msgstr "0 (단순 연결)" msgid "Acceleration of outer walls" msgstr "외벽의 가속도" @@ -8615,10 +8619,10 @@ msgstr "팬 최대 속도 레이어" msgid "" "Fan speed will be ramped up linearly from zero at layer " -"\"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer" -"\". \"full_fan_speed_layer\" will be ignored if lower than " -"\"close_fan_the_first_x_layers\", in which case the fan will be running at " -"maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." +"\"close_fan_the_first_x_layers\" to maximum at layer " +"\"full_fan_speed_layer\". \"full_fan_speed_layer\" will be ignored if lower " +"than \"close_fan_the_first_x_layers\", in which case the fan will be running " +"at maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." msgstr "" "팬 속도는 \"close_fan_the_first_x_layers\" 의 0에서 \"full_fan_speed_layer\" " "의 최고 속도까지 선형적으로 증가합니다. \"full_fan_speed_layer\"가 " @@ -8626,7 +8630,7 @@ msgstr "" "\"close_fan_the_first_x_layers\" + 1 에서 최대 허용 속도로 가도됩니다." msgid "Support interface fan speed" -msgstr "지지대 접점(Interface) 팬 속도" +msgstr "지지대 접점 팬 속도" msgid "" "This fan speed is enforced during all support interfaces, to be able to " @@ -8729,16 +8733,16 @@ msgstr "" "수 있는지를 결정합니다" msgid "Undefine" -msgstr "Undefine" +msgstr "알수없음" msgid "Hardened steel" -msgstr "Hardened steel" +msgstr "경화강 노즐" msgid "Stainless steel" -msgstr "Stainless steel" +msgstr "스테인레스강 노즐" msgid "Brass" -msgstr "Brass" +msgstr "동 노즐" msgid "Nozzle HRC" msgstr "노즐 록웰 경도(HRC)" @@ -8831,7 +8835,7 @@ msgid "The printer cost per hour" msgstr "시간당 프린터 비용" msgid "money/h" -msgstr "원/h" +msgstr "비용/시간" msgid "Support control chamber temperature" msgstr "챔버 온도 제어 지원" @@ -8854,7 +8858,7 @@ msgstr "" "G코드 명령: M106 P3 S(0-255)" msgid "G-code flavor" -msgstr "G코드 호환(Flavor)" +msgstr "G코드 유형" msgid "What kind of gcode the printer is compatible with" msgstr "프린터와 호환되는 G코드 종류" @@ -9176,7 +9180,7 @@ msgstr "" "도/더 작은 너비) 압출로 또는 그 반대로 출력할 때 발생하는 급격한 압출 속도 변" "화를 완화합니다.\n" "\n" -"이는 압출된 체적 유량(mm3/sec)이 시간에 따라 변할 수 있는 최대 속도를 정의합" +"이는 압출된 체적 유량(mm3/초)이 시간에 따라 변할 수 있는 최대 속도를 정의합" "니다. 값이 높을수록 더 높은 압출 속도 변경이 허용되어 속도 전환이 더 빨라진다" "는 의미입니다.\n" "\n" @@ -9186,10 +9190,10 @@ msgstr "" "값이 필요하지 않습니다. 그러나 기능 속도가 크게 달라지는 특정 경우에는 약간" "의 이점을 제공할 수 있습니다. 예를 들어 돌출부로 인해 급격하게 감속이 발생하" "는 경우입니다. 이러한 경우 약 300-350mm3/s2의 높은 값이 권장됩니다. 이렇게 하" -"면 프레셔 어드밴스(Pressure advance)가 더 부드러운 유량 전환을 달성하는 데 도" +"면 프레셔 어드밴스가 더 부드러운 유량 전환을 달성하는 데 도" "움이 될 만큼 충분히 매끄러워질 수 있기 때문입니다.\n" "\n" -"프레셔 어드밴스(Pressure advance) 기능이 없는 느린 프린터의 경우 값을 훨씬 낮" +"프레셔 어드밴스 기능이 없는 느린 프린터의 경우 값을 훨씬 낮" "게 설정해야 합니다. 10-15mm3/s2 값은 직접 구동 압출기의 좋은 시작점이고 보우" "덴 스타일의 경우 5-10mm3/s2입니다.\n" "\n" @@ -9255,6 +9259,8 @@ msgid "" "cooling is enabled, when printing overhangs and when feature speeds are not " "specified explicitly." msgstr "" +"보다 나은 레이어 냉각을 위해 속도를 늦출 때, 돌출부 출력 속도가 명시적으로 지" +"정되지 않은 경우 필라멘트의 최소 출력 속도가 활성화됩니다." msgid "Nozzle diameter" msgstr "노즐 직경" @@ -9395,7 +9401,7 @@ msgid "mm²" msgstr "mm²" msgid "Detect overhang wall" -msgstr "돌출벽(오버행 벽) 감지" +msgstr "돌출벽 감지" #, c-format, boost-format msgid "" @@ -9493,7 +9499,7 @@ msgid "" msgstr "" "퇴출 길이에 비례한 닦기 전 퇴출량(닦기를 시작하기 전에 일부 필라멘트를 퇴출시" "키면 노즐 끝에 녹아있는 소량의 필라멘트만 남게되어 추가 누수 위험이 줄어들 " -"수 있습니다_by MMT)" +"수 있습니다)" msgid "Retract when change layer" msgstr "레이어 변경 시 퇴출" @@ -9591,7 +9597,7 @@ msgid "" "When the retraction is compensated after changing tool, the extruder will " "push this additional amount of filament." msgstr "" -"툴(Tool) 교체 후 퇴출이 보정되면 압출기가 보정된 양 만큼의 추가 필라멘트를 밀" +"툴 교체 후 퇴출이 보정되면 압출기가 보정된 양 만큼의 추가 필라멘트를 밀" "어냅니다." msgid "Retraction Speed" @@ -9601,7 +9607,7 @@ msgid "Speed of retractions" msgstr "퇴출 속도" msgid "Deretraction Speed" -msgstr "퇴출 복귀(디-리트렉션) 속도" +msgstr "퇴출 복귀 속도" msgid "" "Speed for reloading filament into extruder. Zero means same speed with " @@ -9699,10 +9705,10 @@ msgstr "" "은 80%입니다" msgid "Skirt distance" -msgstr "스커트-챙(브림)/개체 거리" +msgstr "스커트 거리" msgid "Distance from skirt to brim or object" -msgstr "스커트와 챙(브림) 또는 개체와의 거리" +msgstr "스커트와 브림 또는 개체와의 거리" msgid "Skirt height" msgstr "스커트 높이" @@ -9757,7 +9763,7 @@ msgid "" "generated model has no seam" msgstr "" "나선화는 외부 윤곽선의 z 이동을 부드럽게 합니다. 그리고 개체를 꽉찬 하단 레이" -"어(Solid bottom layers)가 있는 단일 벽으로 출력합니다. 최종 생성된 출력물에" +"어가 있는 단일 벽으로 출력합니다. 최종 생성된 출력물에" "는 솔기가 없습니다" msgid "" @@ -9901,7 +9907,7 @@ msgid "" "normal(manual) or tree(manual) is selected, only support enforcers are " "generated" msgstr "" -"일반(자동) 및 나무(자동)는 지지대를 자동으로 생성합니다. 일반(수동) 또는 나무" +"일반(자동) 및 나무(자동)은 지지대를 자동으로 생성합니다. 일반(수동) 또는 나무" "(수동) 선택시에는 강제된 지지대만 생성됩니다" msgid "normal(auto)" @@ -10169,18 +10175,18 @@ msgstr "" "으로 계산됩니다 " msgid "Auto brim width" -msgstr "나무 지지대 자동 챙(브림) 너비" +msgstr "나무 지지대 자동 브림 너비" msgid "" "Enabling this option means the width of the brim for tree support will be " "automatically calculated" -msgstr "옵션을 활성화하면 나무 지지대의 챙(브림) 너비가 자동으로 계산됩니다" +msgstr "옵션을 활성화하면 나무 지지대의 브림 너비가 자동으로 계산됩니다" msgid "Tree support brim width" -msgstr "나무 지지대 챙(브림) 너비" +msgstr "나무 지지대 브림 너비" msgid "Distance from tree branch to the outermost brim line" -msgstr "나무 지지대 가지에서 가장 바깥쪽 챙(브림)까지의 거리" +msgstr "나무 지지대 가지에서 가장 바깥쪽 브림까지의 거리" msgid "Tip Diameter" msgstr "끝 직경" @@ -10288,7 +10294,7 @@ msgstr "" "니다" msgid "This gcode is inserted when the extrusion role is changed" -msgstr "" +msgstr "이 G코드는 압출 역할이 변경될 때 삽입됩니다." msgid "" "Line width for top surfaces. If expressed as a %, it will be computed over " @@ -10521,7 +10527,7 @@ msgid "Rotate the polyhole every layer." msgstr "레이어마다 폴리홀을 회전시킵니다." msgid "G-code thumbnails" -msgstr "G코드 미리보기(썸네일)" +msgstr "G코드 미리보기" msgid "" "Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the " @@ -10772,13 +10778,13 @@ msgid "Export Settings" msgstr "설정 내보내기" msgid "Export settings to a file." -msgstr "설정을 파일로 내보냅니다." +msgstr "설정을 파일로 내보내기." msgid "Send progress to pipe" -msgstr "Send progress to pipe" +msgstr "진행 상황을 파이프로 보내기" msgid "Send progress to pipe." -msgstr "Send progress to pipe." +msgstr "진행 상황을 파이프로 보내기." msgid "Arrange Options" msgstr "정렬 옵션" @@ -10793,7 +10799,7 @@ msgid "Repetions count of the whole model" msgstr "전체 모델의 반복 횟수" msgid "Ensure on bed" -msgstr "Ensure on bed" +msgstr "베드에서 확인" msgid "" "Lift the object above the bed when it is partially below. Disabled by default" @@ -11639,10 +11645,10 @@ msgid "PA Calibration" msgstr "PA 교정" msgid "DDE" -msgstr "다이렉트 드라이브(DDE)" +msgstr "다이렉트" msgid "Bowden" -msgstr "보우덴(Bowden)" +msgstr "보우덴" msgid "Extruder type" msgstr "압출기 타입" @@ -12058,8 +12064,8 @@ msgid "" "Did you know that when printing models have a small contact interface with " "the printing surface, it's recommended to use a brim?" msgstr "" -"더 나은 안착을 위한 챙(브림)\n" -"출력 모델이 베드 표면과의 접촉면이 작을 때 챙(브림)를 사용하는 것이 좋다는 사" +"더 나은 안착을 위한 브림\n" +"출력 모델이 베드 표면과의 접촉면이 작을 때 브림를 사용하는 것이 좋다는 사" "실을 알고 있습니까?" #: resources/data/hints.ini: [hint:Set parameters for multiple objects] diff --git a/localization/i18n/ru/OrcaSlicer_ru.po b/localization/i18n/ru/OrcaSlicer_ru.po index 14457462f3..5cb6b67943 100644 --- a/localization/i18n/ru/OrcaSlicer_ru.po +++ b/localization/i18n/ru/OrcaSlicer_ru.po @@ -5,18 +5,17 @@ # msgid "" msgstr "" -"Project-Id-Version: OrcaSlicer V1.8.0 beta1\n" +"Project-Id-Version: OrcaSlicer V1.8.0 Release Candidate 2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-10 14:54+0800\n" -"PO-Revision-Date: 2023-10-31 14:13+0700\n" +"POT-Creation-Date: 2023-08-26 19:45+0800\n" +"PO-Revision-Date: 2023-11-16 20:19+0700\n" "Last-Translator: Andylg \n" "Language-Team: \n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" "X-Generator: Poedit 3.4.1\n" msgid "Supports Painting" @@ -111,13 +110,8 @@ msgid "Lay on face" msgstr "Поверхностью на стол" #, boost-format -msgid "" -"Filament count exceeds the maximum number that painting tool supports. only " -"the first %1% filaments will be available in painting tool." -msgstr "" -"Количество пластиковых нитей превышает максимальное количество " -"поддерживаемое инструментом рисования. Только первые %1% материала будут " -"доступны в инструменте для рисования." +msgid "Filament count exceeds the maximum number that painting tool supports. only the first %1% filaments will be available in painting tool." +msgstr "Количество пластиковых нитей превышает максимальное количество поддерживаемое инструментом рисования. Только первые %1% материала будут доступны в инструменте для рисования." msgid "Color Painting" msgstr "Покраска" @@ -168,14 +162,9 @@ msgid "Triangle" msgstr "Треугольник" msgid "Height Range" -msgstr "Диапазон высоты слоёв" - -# +++++++++++++++++++++ -msgid "Vertical" -msgstr "Вертикальная линия" - -msgid "Horizontal" -msgstr "Горизонтальная линия" +msgstr "" +"Диапазон \n" +"высоты слоёв" msgid "Remove painted color" msgstr "Удаление окрашенного участка" @@ -423,12 +412,8 @@ msgid "Decimate ratio" msgstr "Коэффициент упрощения" #, boost-format -msgid "" -"Processing model '%1%' with more than 1M triangles could be slow. It is " -"highly recommended to simplify the model." -msgstr "" -"Обработка модели '%1%' с более чем 1 млн. треугольников может быть " -"медленной. Настоятельно рекомендуется упростить модель." +msgid "Processing model '%1%' with more than 1M triangles could be slow. It is highly recommended to simplify the model." +msgstr "Обработка модели '%1%' с более чем 1 млн. треугольников может быть медленной. Настоятельно рекомендуется упростить модель." msgid "Simplify model" msgstr "Упростить полигональную сетку" @@ -498,12 +483,27 @@ msgstr "Рисование шва" msgid "Remove selection" msgstr "Удаление выделенного" +msgid "Shift + Mouse move up or dowm" +msgstr "Shift + Мышь вверх или вниз" + +msgid "Rotate text" +msgstr "Поворот текста" + +msgid "Text shape" +msgstr "Форма текста" + msgid "Font" msgstr "Шрифт" msgid "Thickness" msgstr "Толщина" +msgid "Input text" +msgstr "Введите текст" + +msgid "Embeded" +msgstr "Проникновение" + msgid "Text Gap" msgstr "" "Межбуквенный \n" @@ -519,24 +519,12 @@ msgstr "" "Глубина\n" "проникновения" -msgid "Input text" -msgstr "Введите текст" - msgid "Surface" msgstr "На поверхности" msgid "Horizontal text" msgstr "Горизонтальный текст" -msgid "Shift + Mouse move up or dowm" -msgstr "Shift + Мышь вверх или вниз" - -msgid "Rotate text" -msgstr "Поворот текста" - -msgid "Text shape" -msgstr "Форма текста" - msgid "Ctrl+" msgstr "Ctrl+" @@ -566,35 +554,23 @@ msgid "Machine" msgstr "Принтер" msgid "Configuration package was loaded, but some values were not recognized." -msgstr "" -"Пакет конфигурации был загружен, но некоторые значения не были распознаны." +msgstr "Пакет конфигурации был загружен, но некоторые значения не были распознаны." #, boost-format -msgid "" -"Configuration file \"%1%\" was loaded, but some values were not recognized." -msgstr "" -"Файл конфигурации \"%1%\" был загружен, но некоторые значения не были " -"распознаны." +msgid "Configuration file \"%1%\" was loaded, but some values were not recognized." +msgstr "Файл конфигурации \"%1%\" был загружен, но некоторые значения не были распознаны." msgid "V" msgstr "V" -msgid "" -"OrcaSlicer will terminate because of running out of memory.It may be a bug. " -"It will be appreciated if you report the issue to our team." -msgstr "" -"OrcaSlicer завершает работу из-за нехватки памяти. Возможно, это баг " -"программы. Будем признательны, если вы сообщите о проблеме нашей команде." +msgid "OrcaSlicer will terminate because of running out of memory.It may be a bug. It will be appreciated if you report the issue to our team." +msgstr "OrcaSlicer завершает работу из-за нехватки памяти. Возможно, это баг программы. Будем признательны, если вы сообщите о проблеме нашей команде." msgid "Fatal error" msgstr "Критическая ошибка" -msgid "" -"OrcaSlicer will terminate because of a localization error. It will be " -"appreciated if you report the specific scenario this issue happened." -msgstr "" -"OrcaSlicer завершит работу из-за ошибки локализации. Будем признательны, " -"если вы сообщите о конкретном сценарии возникновения этой проблемы." +msgid "OrcaSlicer will terminate because of a localization error. It will be appreciated if you report the specific scenario this issue happened." +msgstr "OrcaSlicer завершит работу из-за ошибки локализации. Будем признательны, если вы сообщите о конкретном сценарии возникновения этой проблемы." msgid "Critical error" msgstr "Критическая ошибка" @@ -617,16 +593,22 @@ msgid "Connect %s failed! [SN:%s, code=%s]" msgstr "Сбой подключения к %s! [Серийный №:%s, код=%s]" msgid "" -"Orca Slicer requires the Microsoft WebView2 Runtime to operate certain " -"features.\n" +"Orca Slicer requires the Microsoft WebView2 Runtime to operate certain features.\n" "Click Yes to install it now." -msgstr "" -"Для работы некоторых функций Orca Slicer требуется Microsoft WebView2 " -"Runtime.Нажмите Да, чтобы установить." +msgstr "Для работы некоторых функций Orca Slicer требуется Microsoft WebView2 Runtime.Нажмите Да, чтобы установить." msgid "WebView2 Runtime" msgstr "WebView2 Runtime" +msgid "" +"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\n" +"OrcaSlicer has attempted to recreate the configuration file.\n" +"Please note, application settings will be lost, but printer profiles will not be affected." +msgstr "" +"Возможно, файл конфигурации OrcaSlicer повреждён и не может быть обработан.\n" +"OrcaSlicer попытался воссоздать файл конфигурации.\n" +"Обратите внимание, что настройки приложения будут потеряны, но профили принтера не будут затронуты." + #, c-format, boost-format msgid "" "%s\n" @@ -643,8 +625,7 @@ msgstr "Загрузка настроек" #, c-format, boost-format msgid "Click to download new version in default browser: %s" -msgstr "" -"Нажмите OK, чтобы загрузить последнюю версию в браузере по умолчанию: %s" +msgstr "Нажмите OK, чтобы загрузить последнюю версию в браузере по умолчанию: %s" msgid "The Orca Slicer needs an upgrade" msgstr "Orca Slice нуждается в обновлении." @@ -655,13 +636,6 @@ msgstr "У вас стоит самая последняя версия." msgid "Info" msgstr "Информация" -msgid "" -"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\n" -"OrcaSlicer has attempted to recreate the configuration file.\n" -"Please note, application settings will be lost, but printer profiles will " -"not be affected." -msgstr "" - msgid "Rebuild" msgstr "Пересоздание" @@ -674,10 +648,6 @@ msgstr "Загрузка режима отображения" msgid "Choose one file (3mf):" msgstr "Выберите один файл (3mf):" -msgid "Choose one or more files (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" -msgstr "" -"Выберите один или несколько файлов (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" - msgid "Choose one or more files (3mf/step/stl/svg/obj/amf):" msgstr "Выберите один или несколько файлов (3mf/step/stl/svg/obj/amf):" @@ -687,12 +657,8 @@ msgstr "Выберите один файл (gcode/3mf):" msgid "Some presets are modified." msgstr "В некоторых профилях имеются изменения." -msgid "" -"You can keep the modifield presets to the new project, discard or save " -"changes as new presets." -msgstr "" -"Вы можете сохранить изменённые профили в новом проекте, отменить или " -"сохранить изменения в новые профили." +msgid "You can keep the modifield presets to the new project, discard or save changes as new presets." +msgstr "Вы можете сохранить изменённые профили в новом проекте, отменить или сохранить изменения в новые профили." msgid "User logged out" msgstr "Пользователь вышел из системы" @@ -703,12 +669,8 @@ msgstr "Создание или открытие файла проекта во msgid "Open Project" msgstr "Открыть проект" -msgid "" -"The version of Orca Slicer is too low and needs to be updated to the latest " -"version before it can be used normally" -msgstr "" -"Слишком старая версия Orca Slicer. Для корректной работы обновите программу " -"до последней версии." +msgid "The version of Orca Slicer is too low and needs to be updated to the latest version before it can be used normally" +msgstr "Слишком старая версия Orca Slicer. Для корректной работы обновите программу до последней версии." msgid "Privacy Policy Update" msgstr "Обновление политики конфиденциальности" @@ -974,8 +936,7 @@ msgid "Assemble" msgstr "Объединить в сборку" msgid "Assemble the selected objects to an object with multiple parts" -msgstr "" -"Объединение выбранных объектов в модель, состоящую из несколько частей." +msgstr "Объединение выбранных объектов в модель, состоящую из несколько частей." msgid "Assemble the selected objects to an object with single part" msgstr "Объединение выбранных моделей в единую." @@ -1152,9 +1113,7 @@ msgid "Click the icon to reset all settings of the object" msgstr "Нажмите на значок, чтобы сбросить все настройки модели" msgid "Right button click the icon to drop the object printable property" -msgstr "" -"Нажмите правой кнопкой мыши на значок, чтобы разрешить/запретить печать " -"модели" +msgstr "Нажмите правой кнопкой мыши на значок, чтобы разрешить/запретить печать модели" msgid "Click the icon to toggle printable property of the object" msgstr "Нажмите на значок, чтобы разрешить/запретить печать модели" @@ -1184,45 +1143,33 @@ msgid "Add Modifier" msgstr "Добавление модификатора" msgid "Switch to per-object setting mode to edit modifier settings." -msgstr "" -"Переключение в режим редактирования настроек печати каждой модели для " -"изменения настроек модификатора." +msgstr "Переключение в режим редактирования настроек печати каждой модели для изменения настроек модификатора." -msgid "" -"Switch to per-object setting mode to edit process settings of selected " -"objects." +msgid "Switch to per-object setting mode to edit process settings of selected objects." msgstr "Переключение в режим редактирования настроек печати каждой модели." msgid "Delete connector from object which is a part of cut" msgstr "Удаление соединения из модели, которое является частью разреза" msgid "Delete solid part from object which is a part of cut" -msgstr "" -"Удаление твердотельной части из модели, которая является частью разреза" +msgstr "Удаление твердотельной части из модели, которая является частью разреза" msgid "Delete negative volume from object which is a part of cut" -msgstr "" -"Удаление объёма для вычитания из модели, которая является частью разреза" +msgstr "Удаление объёма для вычитания из модели, которая является частью разреза" -msgid "" -"To save cut correspondence you can delete all connectors from all related " -"objects." -msgstr "" -"Для сохранения информации о разрезе, вы можете удалить все соединения из " -"всех связанных объектов." +msgid "To save cut correspondence you can delete all connectors from all related objects." +msgstr "Для сохранения информации о разрезе, вы можете удалить все соединения из всех связанных объектов." msgid "" "This action will break a cut correspondence.\n" "After that model consistency can't be guaranteed .\n" "\n" -"To manipulate with solid parts or negative volumes you have to invalidate " -"cut infornation first." +"To manipulate with solid parts or negative volumes you have to invalidate cut infornation first." msgstr "" "Это действие приведёт к удалению информации о разрезе.\n" "После этого согласованность модели не может быть гарантирована.\n" "\n" -"Чтобы манипулировать с твердотельными частями или объёмами для вычитания, " -"необходимо сначала удалить информацию о сделанном разрезе." +"Чтобы манипулировать с твердотельными частями или объёмами для вычитания, необходимо сначала удалить информацию о сделанном разрезе." msgid "Delete all connectors" msgstr "Удалить все соединения" @@ -1278,18 +1225,11 @@ msgstr "Слой" msgid "Selection conflicts" msgstr "Конфликты при выборе" -msgid "" -"If first selected item is an object, the second one should also be object." -msgstr "" -"Если первый выбранный элемент является моделью, то второй также должен быть " -"моделью." +msgid "If first selected item is an object, the second one should also be object." +msgstr "Если первый выбранный элемент является моделью, то второй также должен быть моделью." -msgid "" -"If first selected item is a part, the second one should be part in the same " -"object." -msgstr "" -"Если первый выбранный элемент является частью, то второй должен быть частью " -"той же модели." +msgid "If first selected item is a part, the second one should be part in the same object." +msgstr "Если первый выбранный элемент является частью, то второй должен быть частью той же модели." msgid "The type of the last solid object part is not to be changed." msgstr "Вы не можете изменить тип последнего твердотельного элемента модели." @@ -1338,7 +1278,7 @@ msgstr "Ремонт был отменён" # ??? msgid "Additional process preset" -msgstr "Дополнительная конфигурация процесса" +msgstr "Доп. настройки профиля процесса" msgid "Remove parameter" msgstr "Удалить параметр" @@ -1356,9 +1296,7 @@ msgid "Invalid numeric." msgstr "Неправильное числовое значение." msgid "one cell can only be copied to one or multiple cells in the same column" -msgstr "" -"Одна ячейка может быть скопирована только в одну или несколько ячеек одного " -"и того же столбца." +msgstr "Одна ячейка может быть скопирована только в одну или несколько ячеек одного и того же столбца." msgid "multiple cells copy is not supported" msgstr "копирование нескольких ячеек не поддерживается" @@ -1517,8 +1455,7 @@ msgid "Failed to connect to cloud service" msgstr "Не удалось подключиться к облачному сервису" msgid "Please click on the hyperlink above to view the cloud service status" -msgstr "" -"Для просмотра состояния статуса сервиса нажмите на вышерасположенную ссылку" +msgstr "Для просмотра состояния статуса сервиса нажмите на вышерасположенную ссылку" msgid "Failed to connect to the printer" msgstr "Не удалось подключиться к принтеру." @@ -1535,21 +1472,27 @@ msgstr "Подключение..." msgid "?" msgstr "?" -msgid "/" -msgstr "/" - msgid "Empty" msgstr "Пусто" msgid "AMS" msgstr "АСПП" +msgid "AMS %s" +msgstr "АСПП №%s" + msgid "Auto Refill" msgstr "Дозаправка" msgid "AMS not connected" msgstr "АСПП не подключена" +msgid "Cali" +msgstr "Калиб." + +msgid "Calibration of extrusion" +msgstr "Калибровка экструзии" + msgid "Load Filament" msgstr "Загрузить" @@ -1572,8 +1515,7 @@ msgid "Calibrating AMS..." msgstr "Калибровка АСПП..." msgid "A problem occured during calibration. Click to view the solution." -msgstr "" -"Во время калибровки возникла проблема. Нажмите, чтобы просмотреть решение." +msgstr "Во время калибровки возникла проблема. Нажмите, чтобы просмотреть решение." msgid "Calibrate again" msgstr "Повторить калибровку" @@ -1601,10 +1543,13 @@ msgstr "Вставка нового прутка в экструдер" msgid "Purge old filament" msgstr "Очистка от старого материала" +msgid "Push new filament into the extruder" +msgstr "Вставка нового прутка в экструдер" + msgid "Feed Filament" msgstr "Подача прутка" -# ??? Подтвердить +# ??? Подтвердить, Подтвердите msgid "Confirm extruded" msgstr "Подтверждение экструзии" @@ -1614,12 +1559,11 @@ msgstr "Проверка расположения прутка" msgid "Grab new filament" msgstr "Загрузка нового прутка" -msgid "" -"Choose an AMS slot then press \"Load\" or \"Unload\" button to automatically " -"load or unload filiament." -msgstr "" -"Выберите слот АСПП, затем нажмите кнопку «Загрузить» или «Выгрузить» для " -"автоматической загрузки или выгрузки прутка." +msgid "Confirm whether the filament has been extruded" +msgstr "Подтвердите, что пластиковая нить была выдавлена" + +msgid "Choose an AMS slot then press \"Load\" or \"Unload\" button to automatically load or unload filiament." +msgstr "Выберите слот АСПП, затем нажмите кнопку «Загрузить» или «Выгрузить» для автоматической загрузки или выгрузки прутка." msgid "Edit" msgstr "Правка" @@ -1644,11 +1588,8 @@ msgstr "" msgid "Arranging..." msgstr "Расстановка..." -msgid "" -"Arrange failed. Found some exceptions when processing object geometries." -msgstr "" -"Ошибка расстановки. Обнаружены некоторые исключения при обработке геометрии " -"моделей." +msgid "Arrange failed. Found some exceptions when processing object geometries." +msgstr "Ошибка расстановки. Обнаружены некоторые исключения при обработке геометрии моделей." msgid "Arranging" msgstr "Расстановка" @@ -1656,23 +1597,18 @@ msgstr "Расстановка" msgid "Arranging canceled." msgstr "Расстановка отменена." -msgid "" -"Arranging is done but there are unpacked items. Reduce spacing and try again." -msgstr "" -"Расстановка завершена, но не всё уместилось на столе. Уменьшите интервал " -"расстановки и повторите попытку." +msgid "Arranging is done but there are unpacked items. Reduce spacing and try again." +msgstr "Расстановка завершена, но не всё уместилось на столе. Уменьшите интервал расстановки и повторите попытку." msgid "Arranging done." msgstr "Расстановка выполнена." #, c-format, boost-format msgid "" -"Arrangement ignored the following objects which can't fit into a single " -"bed:\n" +"Arrangement ignored the following objects which can't fit into a single bed:\n" "%s" msgstr "" -"При расстановке были проигнорированы следующие модели, которые не помещаются " -"на одном столе:\n" +"При расстановке были проигнорированы следующие модели, которые не помещаются на одном столе:\n" "%s" msgid "" @@ -1716,6 +1652,21 @@ msgstr "Авторизация" msgid "Login failed" msgstr "Ошибка авторизации" +msgid "The region parameter is incorrrect" +msgstr "Неправильная региональная настройка" + +msgid "Failure of printer login" +msgstr "Не удалось подключиться к принтеру." + +msgid "Failed to get ticket" +msgstr "Не удалось получить заявку" + +msgid "User authorization timeout" +msgstr "Таймаут авторизации пользователя" + +msgid "Failure of bind" +msgstr "Ошибка привязки" + msgid "Please check the printer network connection." msgstr "Пожалуйста, проверьте сетевое подключение принтера." @@ -1726,55 +1677,49 @@ msgid "Task canceled." msgstr "Задание отменено." msgid "Upload task timed out. Please check the network status and try again." -msgstr "" -"Истекло время ожидания отправки задания. Проверьте сетевое подключение и " -"повторите попытку." +msgstr "Истекло время ожидания отправки задания. Проверьте сетевое подключение и повторите попытку." msgid "Cloud service connection failed. Please try again." -msgstr "" -"Не удалось подключиться к облачному сервису. Пожалуйста, попробуйте ещё раз." +msgstr "Не удалось подключиться к облачному сервису. Пожалуйста, попробуйте ещё раз." msgid "Print file not found. please slice again." msgstr "Файл для печати не найден, нарежьте ещё раз." -msgid "" -"The print file exceeds the maximum allowable size (1GB). Please simplify the " -"model and slice again." -msgstr "" -"Файл для печати превышает максимально допустимый размер (1 ГБ). Пожалуйста, " -"упростите модель и нарежьте ещё раз." +msgid "Print file not found, please slice again" +msgstr "Файл для печати не найден, нарежьте ещё раз." + +msgid "The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again." +msgstr "Файл для печати превышает максимально допустимый размер (1 ГБ). Пожалуйста, упростите модель и нарежьте ещё раз." + +msgid "Failed uploading print file" +msgstr "Не удалось передать файл на печать." + +msgid "Wrong Access code" +msgstr "Неправильный код доступа" msgid "Failed to send the print job. Please try again." -msgstr "" -"Не удалось отправить задание на печать. Пожалуйста, попробуйте ещё раз." +msgstr "Не удалось отправить задание на печать. Пожалуйста, попробуйте ещё раз." + +msgid "Send to Printer failed. Please try again." +msgstr "Ошибка отправки на принтер. Пожалуйста, попробуйте ещё раз." + +msgid "No space left on Printer SD card" +msgstr "На SD-карте принтера недостаточно места" msgid "Failed to upload file to ftp. Please try again." msgstr "Не удалось загрузить файл на FTP. Пожалуйста, попробуйте ещё раз." -msgid "" -"Check the current status of the bambu server by clicking on the link above." -msgstr "" -"Проверить текущий статус сервера Bambu можно перейдя по указанной выше " -"ссылке." +msgid "Check the current status of the bambu server by clicking on the link above." +msgstr "Проверить текущий статус сервера Bambu можно перейдя по указанной выше ссылке." -msgid "" -"The size of the print file is too large. Please adjust the file size and try " -"again." -msgstr "" -"Размер файла для печати слишком велик. Пожалуйста, уменьшите размер файла и " -"повторите попытку." +msgid "The size of the print file is too large. Please adjust the file size and try again." +msgstr "Размер файла для печати слишком велик. Пожалуйста, уменьшите размер файла и повторите попытку." msgid "Print file not found, Please slice it again and send it for printing." -msgstr "" -"Файл печати не найден. Пожалуйста, нарежьте его ещё раз и отправьте на " -"печать." +msgstr "Файл печати не найден. Пожалуйста, нарежьте его ещё раз и отправьте на печать." -msgid "" -"Failed to upload print file to FTP. Please check the network status and try " -"again." -msgstr "" -"Не удалось загрузить файл печати на FTP. Проверьте состояние сети и " -"повторите попытку." +msgid "Failed to upload print file to FTP. Please check the network status and try again." +msgstr "Не удалось загрузить файл печати на FTP. Проверьте состояние сети и повторите попытку." msgid "Sending print job over LAN" msgstr "Отправка задания на печать по локальной сети" @@ -1793,13 +1738,11 @@ msgstr "Отправка конфигурации печати" #, c-format, boost-format msgid "Successfully sent. Will automatically jump to the device page in %ss" -msgstr "" -"Успешно отправлено. Автоматический переход на страницу устройства через %s с." +msgstr "Успешно отправлено. Автоматический переход на страницу устройства через %s с." #, c-format, boost-format msgid "Successfully sent. Will automatically jump to the next page in %ss" -msgstr "" -"Успешно отправлено. Автоматический переход на следующую страницу через %s с." +msgstr "Успешно отправлено. Автоматический переход на следующую страницу через %s с." msgid "An SD card needs to be inserted before printing via LAN." msgstr "Перед печатью через локальную сеть необходимо вставить SD-карту." @@ -1807,6 +1750,9 @@ msgstr "Перед печатью через локальную сеть нео msgid "Sending gcode file over LAN" msgstr "Отправка файла G-кода по локальной сети" +msgid "Sending gcode file through cloud service" +msgstr "Отправка файла G-кода через облачный сервис" + msgid "Sending gcode file to sdcard" msgstr "Отправка файла G-кода на SD-карту" @@ -1817,6 +1763,9 @@ msgstr "Успешно отправлено. Закрытие текущей с msgid "An SD card needs to be inserted before sending to printer." msgstr "Перед отправкой на принтер необходимо вставить SD-карту." +msgid "Please log out and login to the printer again." +msgstr "Пожалуйста, выйдите и снова войдите в принтер." + msgid "Choose SLA archive:" msgstr "Выберите SLA архив:" @@ -1844,12 +1793,8 @@ msgstr "Скорость" msgid "Importing SLA archive" msgstr "Импорт SLA архива" -msgid "" -"The SLA archive doesn't contain any presets. Please activate some SLA " -"printer preset first before importing that SLA archive." -msgstr "" -"Архив SLA не содержит никаких профилей. Пожалуйста, сначала активируйте " -"какой-нибудь профиль SLA принтера, прежде чем импортировать этот SLA архив." +msgid "The SLA archive doesn't contain any presets. Please activate some SLA printer preset first before importing that SLA archive." +msgstr "Архив SLA не содержит никаких профилей. Пожалуйста, сначала активируйте какой-нибудь профиль SLA принтера, прежде чем импортировать этот SLA архив." msgid "Importing canceled." msgstr "Импорт отменен." @@ -1857,12 +1802,8 @@ msgstr "Импорт отменен." msgid "Importing done." msgstr "Импорт завершён." -msgid "" -"The imported SLA archive did not contain any presets. The current SLA " -"presets were used as fallback." -msgstr "" -"Импортированный SLA архив не содержит никаких профилей. Текущие SLA профили " -"использовались в качестве резервных." +msgid "The imported SLA archive did not contain any presets. The current SLA presets were used as fallback." +msgstr "Импортированный SLA архив не содержит никаких профилей. Текущие SLA профили использовались в качестве резервных." msgid "You cannot load SLA project with a multi-part object on the bed" msgstr "Вы не можете загрузить SLA проект с составной моделью на столе" @@ -1906,25 +1847,14 @@ msgstr "Orca Slicer распространяется под лицензией " msgid "GNU Affero General Public License, version 3" msgstr "GNU Affero General Public третьей версии" -msgid "" -"Orca Slicer is based on BambuStudio by Bambulab, which is from PrusaSlicer " -"by Prusa Research. PrusaSlicer is from Slic3r by Alessandro Ranellucci and " -"the RepRap community" -msgstr "" -"Orca Slicer основан на BambuStudio от компании Bambulab, которая взяла за " -"основу PrusaSlicer от компании Prusa Research. PrusaSlicer же основан на " -"Slic3r от Alessandro Ranellucci и разработках сообщества RepRap." +msgid "Orca Slicer is based on BambuStudio by Bambulab, which is from PrusaSlicer by Prusa Research. PrusaSlicer is from Slic3r by Alessandro Ranellucci and the RepRap community" +msgstr "Orca Slicer основан на BambuStudio от компании Bambulab, которая взяла за основу PrusaSlicer от компании Prusa Research. PrusaSlicer же основан на Slic3r от Alessandro Ranellucci и разработках сообщества RepRap." msgid "Libraries" msgstr "Библиотеки" -msgid "" -"This software uses open source components whose copyright and other " -"proprietary rights belong to their respective owners" -msgstr "" -"В данном программном обеспечении используются компоненты с открытым исходным " -"кодом, авторские и иные права на которые принадлежат их соответствующим " -"владельцам." +msgid "This software uses open source components whose copyright and other proprietary rights belong to their respective owners" +msgstr "В данном программном обеспечении используются компоненты с открытым исходным кодом, авторские и иные права на которые принадлежат их соответствующим владельцам." #, c-format, boost-format msgid "About %s" @@ -1937,18 +1867,13 @@ msgid "OrcaSlicer is based on BambuStudio, PrusaSlicer, and SuperSlicer." msgstr "OrcaSlicer основан на проектах BambuStudio, PrusaSlicer и SuperSlicer." msgid "BambuStudio is originally based on PrusaSlicer by PrusaResearch." -msgstr "" -"BambuStudio изначально основан на PrusaSlicer от компании PrusaResearch." +msgstr "BambuStudio изначально основан на PrusaSlicer от компании PrusaResearch." msgid "PrusaSlicer is originally based on Slic3r by Alessandro Ranellucci." msgstr "PrusaSlicer основан на проекте Slic3r от Alessandro Ranellucci." -msgid "" -"Slic3r was created by Alessandro Ranellucci with the help of many other " -"contributors." -msgstr "" -"Slic3r был создан Alessandro Ranellucci совместно с многими другими " -"участниками сообщества." +msgid "Slic3r was created by Alessandro Ranellucci with the help of many other contributors." +msgstr "Slic3r был создан Alessandro Ranellucci совместно с многими другими участниками сообщества." msgid "Version" msgstr "Версия" @@ -1976,7 +1901,7 @@ msgid "max" msgstr "макс." msgid "min" -msgstr "мин" +msgstr "мин." #, boost-format msgid "The input value should be greater than %1% and less than %2%" @@ -1988,6 +1913,9 @@ msgstr "Серийный №" msgid "Setting AMS slot information while printing is not supported" msgstr "Изменение информации о слотах АСПП во время печати не поддерживается" +msgid "Factors of dynamic flow cali" +msgstr "Коэф. калиб. динам. потока" + msgid "Factors of Flow Dynamics Calibration" msgstr "Коэф. калиб. динам. потока" @@ -2001,8 +1929,7 @@ msgid "Factor N" msgstr "Коэф. N" msgid "Setting Virtual slot information while printing is not supported" -msgstr "" -"Настройка информации виртуального слота во время печати не поддерживается" +msgstr "Настройка информации виртуального слота во время печати не поддерживается" msgid "Are you sure you want to clear the filament information?" msgstr "Вы уверены, что хотите удалить информацию о пластиковой нити?" @@ -2011,12 +1938,10 @@ msgid "You need to select the material type and color first." msgstr "Сначала необходимо выбрать тип материала и цвет." msgid "Please input a valid value (K in 0~0.5)" -msgstr "Пожалуйста, введите допустимое значение (K в диапазоне 0~0,5)" +msgstr "Пожалуйста, введите допустимое значение (K в диапазоне 0~0.5)" msgid "Please input a valid value (K in 0~0.5, N in 0.6~2.0)" -msgstr "" -"Пожалуйста, введите допустимое значение (K в диапазоне 0~0,5, N в диапазоне " -"0,6~2,0)" +msgstr "Пожалуйста, введите допустимое значение (K в диапазоне 0~0.5, N в диапазоне 0.6~2.0)" msgid "Other Color" msgstr "Другой цвет" @@ -2027,15 +1952,8 @@ msgstr "Пользовательский цвет" msgid "Dynamic flow calibration" msgstr "Калибровка динамики потока" -msgid "" -"The nozzle temp and max volumetric speed will affect the calibration " -"results. Please fill in the same values as the actual printing. They can be " -"auto-filled by selecting a filament preset." -msgstr "" -"Температура сопла и максимальная объёмная скорость влияют на результаты " -"калибровки. Введите те же значения, которые вы используете при фактической " -"печати. Их можно заполнить автоматически, выбрав существующий профиль " -"пластиковой нити." +msgid "The nozzle temp and max volumetric speed will affect the calibration results. Please fill in the same values as the actual printing. They can be auto-filled by selecting a filament preset." +msgstr "Температура сопла и максимальная объёмная скорость влияют на результаты калибровки. Введите те же значения, которые вы используете при фактической печати. Их можно заполнить автоматически, выбрав существующий профиль пластиковой нити." msgid "Nozzle Diameter" msgstr "Диаметр сопла" @@ -2067,14 +1985,8 @@ msgstr "Запустить калибровку" msgid "Next" msgstr "Далее" -msgid "" -"Calibration completed. Please find the most uniform extrusion line on your " -"hot bed like the picture below, and fill the value on its left side into the " -"factor K input box." -msgstr "" -"Калибровка завершена. Теперь найдите на столе наиболее равномерно " -"экструдированную линию, как показано на рисунке ниже, и введите это значение " -"в поле ввода коэффициента K." +msgid "Calibration completed. Please find the most uniform extrusion line on your hot bed like the picture below, and fill the value on its left side into the factor K input box." +msgstr "Калибровка завершена. Теперь найдите на столе наиболее равномерно экструдированную линию, как показано на рисунке ниже, и введите это значение в поле ввода коэффициента K." msgid "Save" msgstr "Сохранить" @@ -2105,11 +2017,9 @@ msgstr "Шаг" msgid "AMS Slots" msgstr "Слоты АСПП" -msgid "" -"Note: Only the AMS slots loaded with the same material type can be selected." -msgstr "" -"Примечание: можно выбрать только слоты АСПП, загруженные одним и тем же " -"типом материала." +# ??? Замечание: разрешён выбор только слотов АСПП с одинаковым типом материала. +msgid "Note: Only the AMS slots loaded with the same material type can be selected." +msgstr "Замечание: можно выбирать только слоты АСПП с одинаковым типом материала." msgid "Enable AMS" msgstr "Включить АСПП" @@ -2126,40 +2036,20 @@ msgstr "Печать материалом, установленным на за msgid "Cabin humidity" msgstr "Влажность внутри" -msgid "" -"Green means that AMS humidity is normal, orange represent humidity is high, " -"red represent humidity is too high.(Hygrometer: lower the better.)" -msgstr "" -"Зелёный цвет означает, что влажность в системе АСПП нормальная, оранжевый - " -"высокая, красный - слишком высокая. Чем ниже значение гигрометра, тем лучше." +msgid "Green means that AMS humidity is normal, orange represent humidity is high, red represent humidity is too high.(Hygrometer: lower the better.)" +msgstr "Зелёный цвет означает, что влажность в системе АСПП нормальная, оранжевый - высокая, красный - слишком высокая. Чем ниже значение гигрометра, тем лучше." msgid "Desiccant status" msgstr "Состояние влагопоглотителя" -msgid "" -"A desiccant status lower than two bars indicates that desiccant may be " -"inactive. Please change the desiccant.(The bars: higher the better.)" -msgstr "" -"Состояние влагопоглотителя (силикагеля) ниже двух делений указывает на то, " -"что он может уже не выполнять свою функцию. Пожалуйста, замените его. Чем " -"больше делений на индикаторе, тем лучше." +msgid "A desiccant status lower than two bars indicates that desiccant may be inactive. Please change the desiccant.(The bars: higher the better.)" +msgstr "Состояние влагопоглотителя (силикагеля) ниже двух делений указывает на то, что он может уже не выполнять свою функцию. Пожалуйста, замените его. Чем больше делений на индикаторе, тем лучше." -msgid "" -"Note: When the lid is open or the desiccant pack is changed, it can take " -"hours or a night to absorb the moisture. Low temperatures also slow down the " -"process. During this time, the indicator may not represent the chamber " -"accurately." -msgstr "" -"Примечание: при открытой крышке или заменённом влагопоглотителе, может " -"потребоваться несколько часов или ночь для поглощения влаги. Кроме того, " -"процесс может замедлиться из-за низкой температуры окружающей среды. В " -"течение этого времени значения индикатора могут быть неточными." +msgid "Note: When the lid is open or the desiccant pack is changed, it can take hours or a night to absorb the moisture. Low temperatures also slow down the process. During this time, the indicator may not represent the chamber accurately." +msgstr "Примечание: при открытой крышке или заменённом влагопоглотителе, может потребоваться несколько часов или ночь для поглощения влаги. Кроме того, процесс может замедлиться из-за низкой температуры окружающей среды. В течение этого времени значения индикатора могут быть неточными." -msgid "" -"Config which AMS slot should be used for a filament used in the print job" -msgstr "" -"Задайте слот АСПП, который должен использоваться для прутка, используемого в " -"текущем задании." +msgid "Config which AMS slot should be used for a filament used in the print job" +msgstr "Задайте слот АСПП, который должен использоваться для прутка, используемого в текущем задании." msgid "Filament used in this print job" msgstr "Пруток используемый в этом задании" @@ -2174,8 +2064,7 @@ msgid "Do not Enable AMS" msgstr "Не включать АСПП" msgid "Print using materials mounted on the back of the case" -msgstr "" -"Печать с использованием материала, установленного на задней части корпуса" +msgstr "Печать с использованием материала, установленного на задней части корпуса" msgid "Print with filaments in ams" msgstr "" @@ -2185,35 +2074,18 @@ msgstr "" msgid "Print with filaments mounted on the back of the chassis" msgstr "Печать материалами, установленными на задней части корпуса." -msgid "" -"When the current material run out, the printer will continue to print in the " -"following order." -msgstr "" -"Когда текущий материал закончится, принтер продолжит печать в указанном " -"порядке." +msgid "When the current material run out, the printer will continue to print in the following order." +msgstr "Когда текущий материал закончится, принтер продолжит печать в указанном порядке." msgid "Group" msgstr "Группа" -msgid "The printer does not currently support auto refill." -msgstr "В настоящее время принтер не поддерживает функцию автодозаправки." - msgid "" -"AMS filament backup is not enabled, please enable it in the AMS settings." +"There are currently no identical spare consumables available, and automatic replenishment is currently not possible. \n" +"(Currently supporting automatic supply of consumables with the same brand, material type, and color)" msgstr "" -"Резервирование материала АСПП не включено, пожалуйста, включите его в " -"настройках АСПП." - -msgid "" -"If there are two identical filaments in AMS, AMS filament backup will be " -"enabled. \n" -"(Currently supporting automatic supply of consumables with the same brand, " -"material type, and color)" -msgstr "" -"Если в АСПП имеются два одинаковых материала, то будет включено " -"резервирование материала АСПП. \n" -"(В настоящее время поддерживается автоматическая поставка расходных " -"материалов того же производителя, типа материала и цвета)." +"В настоящее время одинаковые материалы отсутствуют, поэтому функция резервирования материала (автодозаправка) недоступна. \n" +"(В настоящее время поддерживается автоматическая дозаправка материала только одного производителя, типа и цвета)" msgid "AMS Settings" msgstr "Настройки АСПП" @@ -2221,70 +2093,35 @@ msgstr "Настройки АСПП" msgid "Insertion update" msgstr "Обновление при вставке материала" -msgid "" -"The AMS will automatically read the filament information when inserting a " -"new Bambu Lab filament. This takes about 20 seconds." -msgstr "" -"АСПП автоматически считывает информацию о материале при установке новой " -"катушки Bambu. Это занимает около 20 секунд." +msgid "The AMS will automatically read the filament information when inserting a new Bambu Lab filament. This takes about 20 seconds." +msgstr "АСПП автоматически считывает информацию о материале при установке новой катушки Bambu. Это занимает около 20 секунд." -msgid "" -"Note: if new filament is inserted during printing, the AMS will not " -"automatically read any information until printing is completed." -msgstr "" -"Примечание: если во время печати вставляется новая пластиковая нить, АСПП " -"автоматически считает информацию о ней только по завершению печати." +msgid "Note: if new filament is inserted during printing, the AMS will not automatically read any information until printing is completed." +msgstr "Примечание: если во время печати вставляется новая пластиковая нить, АСПП автоматически считает информацию о ней только по завершению печати." -msgid "" -"When inserting a new filament, the AMS will not automatically read its " -"information, leaving it blank for you to enter manually." -msgstr "" -"При вставке новой пластиковой нити, АСПП не будет автоматически считывать " -"информацию о ней, оставляя поле пустым, чтобы пользователь мог ввести данные " -"о ней вручную." +msgid "When inserting a new filament, the AMS will not automatically read its information, leaving it blank for you to enter manually." +msgstr "При вставке новой пластиковой нити, АСПП не будет автоматически считывать информацию о ней, оставляя поле пустым, чтобы пользователь мог ввести данные о ней вручную." msgid "Power on update" msgstr "Обновление при включении принтера" -msgid "" -"The AMS will automatically read the information of inserted filament on " -"start-up. It will take about 1 minute.The reading process will roll filament " -"spools." -msgstr "" -"При каждом включении принтера АСПП будет автоматически считывать информация " -"о вставленных материалах. Это занимает приблизительно одну минуту. В " -"процессе считывания информации о материале катушка вращается." +msgid "The AMS will automatically read the information of inserted filament on start-up. It will take about 1 minute.The reading process will roll filament spools." +msgstr "При каждом включении принтера АСПП будет автоматически считывать информация о вставленных материалах. Это занимает приблизительно одну минуту. В процессе считывания информации о материале катушка вращается." -msgid "" -"The AMS will not automatically read information from inserted filament " -"during startup and will continue to use the information recorded before the " -"last shutdown." -msgstr "" -"При каждом включении принтера, считывание информации о вставленных " -"материалах АСПП будет отключено. Будет использоваться информация, полученная " -"перед последним выключением." +msgid "The AMS will not automatically read information from inserted filament during startup and will continue to use the information recorded before the last shutdown." +msgstr "При каждом включении принтера, считывание информации о вставленных материалах АСПП будет отключено. Будет использоваться информация, полученная перед последним выключением." msgid "Update remaining capacity" msgstr "Обновлять оставшуюся ёмкость катушки" -msgid "" -"The AMS will estimate Bambu filament's remaining capacity after the filament " -"info is updated. During printing, remaining capacity will be updated " -"automatically." -msgstr "" -"АСПП считывает информацию о расходуемом материале Bambu и рассчитывает его " -"остаточную ёмкость на катушке. Остаточная ёмкость обновляется автоматически " -"в процессе печати." +msgid "The AMS will estimate Bambu filament's remaining capacity after the filament info is updated. During printing, remaining capacity will be updated automatically." +msgstr "АСПП считывает информацию о расходуемом материале Bambu и рассчитывает его остаточную ёмкость на катушке. Остаточная ёмкость обновляется автоматически в процессе печати." msgid "AMS filament backup" msgstr "Резервирование материала АСПП" -msgid "" -"AMS will continue to another spool with the same properties of filament " -"automatically when current filament runs out" -msgstr "" -"АСПП автоматически переключится на другую катушку с тем же типом материала, " -"когда текущий закончится." +msgid "AMS will continue to another spool with the same properties of filament automatically when current filament runs out" +msgstr "АСПП автоматически переключится на другую катушку с тем же типом материала, когда текущий закончится." msgid "File" msgstr "Файл" @@ -2292,19 +2129,11 @@ msgstr "Файл" msgid "Calibration" msgstr "Калибровка" -msgid "" -"Failed to download the plug-in. Please check your firewall settings and vpn " -"software, check and retry." -msgstr "" -"Не удалось загрузить плагин. Пожалуйста, проверьте настройки брандмауэра и " -"vpn, и повторите попытку." +msgid "Failed to download the plug-in. Please check your firewall settings and vpn software, check and retry." +msgstr "Не удалось загрузить плагин. Пожалуйста, проверьте настройки брандмауэра и vpn, и повторите попытку." -msgid "" -"Failed to install the plug-in. Please check whether it is blocked or deleted " -"by anti-virus software." -msgstr "" -"Не удалось установить плагин. Пожалуйста, проверьте, не заблокирован ли он " -"или не удалён антивирусом." +msgid "Failed to install the plug-in. Please check whether it is blocked or deleted by anti-virus software." +msgstr "Не удалось установить плагин. Пожалуйста, проверьте, не заблокирован ли он или не удалён антивирусом." msgid "click here to see more info" msgstr "нажмите здесь, чтобы увидеть больше информации" @@ -2312,22 +2141,14 @@ msgstr "нажмите здесь, чтобы увидеть больше инф msgid "Please home all axes (click " msgstr "Пожалуйста, припаркуйте все оси в начало координат (нажав " -msgid "" -") to locate the toolhead's position. This prevents device moving beyond the " -"printable boundary and causing equipment wear." -msgstr "" -") для определения положения печатной головы. Это предотвращает перемещение " -"за пределы области печати и износ оборудования." +msgid ") to locate the toolhead's position. This prevents device moving beyond the printable boundary and causing equipment wear." +msgstr ") для определения положения печатной головы. Это предотвращает перемещение за пределы области печати и износ оборудования." msgid "Go Home" msgstr "На главную" -msgid "" -"A error occurred. Maybe memory of system is not enough or it's a bug of the " -"program" -msgstr "" -"Произошла ошибка. Возможно, недостаточно системной памяти или это баг " -"программы." +msgid "A error occurred. Maybe memory of system is not enough or it's a bug of the program" +msgstr "Произошла ошибка. Возможно, недостаточно системной памяти или это баг программы." msgid "Please save project and restart the program. " msgstr "Пожалуйста, сохраните проект и перезапустите программу. " @@ -2380,15 +2201,11 @@ msgid "Running post-processing scripts" msgstr "Запуск скриптов постобработки" msgid "Copying of the temporary G-code to the output G-code failed" -msgstr "" -"Не удалось скопировать временный G-код в местонахождение выходного файла G-" -"кода" +msgstr "Не удалось скопировать временный G-код в местонахождение выходного файла G-кода" #, boost-format msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" -msgstr "" -"Планирование загрузки на `%1%`. Смотрите Окна -> Очередь загрузки на хост " -"печати" +msgstr "Планирование загрузки на `%1%`. Смотрите Окна -> Очередь загрузки на хост печати" msgid "Origin" msgstr "Начало координат" @@ -2399,18 +2216,11 @@ msgstr "Диаметр" msgid "Size in X and Y of the rectangular plate." msgstr "Размеры прямоугольного стола в XY координатах." -msgid "" -"Distance of the 0,0 G-code coordinate from the front left corner of the " -"rectangle." -msgstr "" -"Расстояние до точки начало координат. Отсчёт от левого переднего угла " -"прямоугольного стола." +msgid "Distance of the 0,0 G-code coordinate from the front left corner of the rectangle." +msgstr "Расстояние до точки начало координат. Отсчёт от левого переднего угла прямоугольного стола." -msgid "" -"Diameter of the print bed. It is assumed that origin (0,0) is located in the " -"center." -msgstr "" -"Диаметр стола. Предполагается, что начало координат (0,0) находится в центре." +msgid "Diameter of the print bed. It is assumed that origin (0,0) is located in the center." +msgstr "Диаметр стола. Предполагается, что начало координат (0,0) находится в центре." msgid "Rectangular" msgstr "Прямоугольная" @@ -2451,11 +2261,8 @@ msgstr "Ошибка! Недопустимая модель" msgid "The selected file contains no geometry." msgstr "Выбранный файл не содержит геометрии." -msgid "" -"The selected file contains several disjoint areas. This is not supported." -msgstr "" -"Выбранный файл содержит несколько не пересекающихся областей. Такие файлы не " -"поддерживаются." +msgid "The selected file contains several disjoint areas. This is not supported." +msgstr "Выбранный файл содержит несколько не пересекающихся областей. Такие файлы не поддерживаются." msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "Выберите файл для импорта текстуры стола из PNG/SVG:" @@ -2475,13 +2282,26 @@ msgstr "" "Пожалуйста, убедитесь, что вы задали нужную температуру для печати.\n" "\n" +#, c-format, boost-format +msgid "Recommended nozzle temperature of this filament type is [%d, %d] degree centigrade" +msgstr "Рекомендуемая температура сопла для данного типа пластиковой нити составляет [%d, %d] градусов Цельсия." + #, c-format, boost-format msgid "" -"Recommended nozzle temperature of this filament type is [%d, %d] degree " -"centigrade" +"Bed temperature of other layer is lower than bed temperature of initial layer for more than %d degree centigrade.\n" +"This may cause model broken free from build plate during printing" msgstr "" -"Рекомендуемая температура сопла для данного типа пластиковой нити составляет " -"[%d, %d] градусов Цельсия." +"Температура стола для последующих слоёв слишком низкая по сравнению с температурой первого слоя, более чем на %d градусов Цельсия.\n" +"Это может привести к отрыву модели от стола во время печати." + +msgid "" +"Bed temperature is higher than vitrification temperature of this filament.\n" +"This may cause nozzle blocked and printing failure\n" +"Please keep the printer open during the printing process to ensure air circulation or reduce the temperature of the hot bed" +msgstr "" +"Температура стола выше температуры стеклования этой пластиковой нити.\n" +"Это может привести к засорению сопла и сбою печати.\n" +"Пожалуйста, держите принтер открытым во время печати, чтобы обеспечить циркуляцию воздуха или снизить температуру стола." msgid "" "Too small max volumetric speed.\n" @@ -2490,16 +2310,6 @@ msgstr "" "Слишком маленькая максимальная объёмная скорость.\n" "Сбросьте до 0,5." -#, c-format, boost-format -msgid "" -"Current chamber temperature is higher than the material's safe temperature," -"it may result in material softening and clogging.The maximum safe " -"temperature for the material is %d" -msgstr "" -"Текущая температура в камере превышает безопасную температуру для этого " -"материала, что может привести к размягчению материала или засорению " -"экструдера. Безопасная температура текущего материала составляет %d." - msgid "" "Too small layer height.\n" "Reset to 0.2" @@ -2524,19 +2334,15 @@ msgstr "" "Высота первого слоя будет сброшена до 0,2." msgid "" -"This setting is only used for model size tunning with small value in some " -"cases.\n" +"This setting is only used for model size tunning with small value in some cases.\n" "For example, when model size has small error and hard to be assembled.\n" "For large size tuning, please use model scale function.\n" "\n" "The value will be reset to 0." msgstr "" -"Этот параметр используется только для точной корректировки размера модели в " -"определенных случаях.\n" -"Например, когда есть небольшая погрешность в размерах модели и её трудно " -"собрать.\n" -"Для более значительной корректировки размеров используйте функцию " -"масштабирования модели.\n" +"Этот параметр используется только для точной корректировки размера модели в определенных случаях.\n" +"Например, когда есть небольшая погрешность в размерах модели и её трудно собрать.\n" +"Для более значительной корректировки размеров используйте функцию масштабирования модели.\n" "\n" "Это значение будет сброшено на 0." @@ -2548,18 +2354,14 @@ msgid "" "The value will be reset to 0." msgstr "" "Слишком большая компенсация слоновьей ноги нецелесообразна.\n" -"Если имеется серьёзный эффект слоновьей ноги, проверьте другие настройки " -"печати.\n" +"Если имеется серьёзный эффект слоновьей ноги, проверьте другие настройки печати.\n" "Например, не слишком ли высокая температура стола.\n" "\n" "Значение будет сброшено на 0." -msgid "" -"Spiral mode only works when wall loops is 1, support is disabled, top shell " -"layers is 0, sparse infill density is 0 and timelapse type is traditional." +msgid "Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional." msgstr "" -"Для режима печати «Спиральная ваза» необходимо чтобы соблюдались следующие " -"условия:\n" +"Для режима печати «Спиральная ваза» необходимо чтобы соблюдались следующие условия:\n" "- одностеночный периметр\n" "- отсутствие поддержки\n" "- отсутствие верхних сплошных слоёв\n" @@ -2567,9 +2369,6 @@ msgstr "" "- отключено «Обнаружение тонких стенок»\n" "- Режим записи таймлапсов - обычный режим" -msgid " But machines with I3 structure will not generate timelapse videos." -msgstr " Но принтеры с кинематикой I3 не будут писать таймлапс." - msgid "" "Change these settings automatically? \n" "Yes - Change these settings and enable spiral mode automatically\n" @@ -2580,14 +2379,12 @@ msgstr "" "Нет - Отказаться от использования режима «Спиральная ваза»" msgid "" -"Prime tower does not work when Adaptive Layer Height or Independent Support " -"Layer Height is on.\n" +"Prime tower does not work when Adaptive Layer Height or Independent Support Layer Height is on.\n" "Which do you want to keep?\n" "YES - Keep Prime Tower\n" "NO - Keep Adaptive Layer Height and Independent Support Layer Height" msgstr "" -"Черновая башня не работает, когда включена функция «Переменная высота слоёв» " -"или «Независимая высота слоя поддержки»\n" +"Черновая башня не работает, когда включена функция «Переменная высота слоёв» или «Независимая высота слоя поддержки»\n" "Что вы хотите сохранить?\n" "ДА - Сохранить черновую башню\n" "НЕТ - Сохранить переменную высоту слоя и независимую высоту слоя поддержки" @@ -2598,8 +2395,7 @@ msgid "" "YES - Keep Prime Tower\n" "NO - Keep Adaptive Layer Height" msgstr "" -"Черновая башня не работает, когда включена функция «Переменная высота " -"слоёв».\n" +"Черновая башня не работает, когда включена функция «Переменная высота слоёв».\n" "Что вы хотите сохранить?\n" "Да - Сохранить черновую башню\n" "Нет - Сохранить переменную высоту слоёв" @@ -2610,8 +2406,7 @@ msgid "" "YES - Keep Prime Tower\n" "NO - Keep Independent Support Layer Height" msgstr "" -"Черновая башня не работает, если включена функция «Независимая высота слоя " -"поддержки»\n" +"Черновая башня не работает, если включена функция «Независимая высота слоя поддержки»\n" "Что вы хотите сохранить?\n" "ДА - Сохранить черновую башню\n" "НЕТ - Сохранить независимую высоту слоя поддержки" @@ -2627,8 +2422,7 @@ msgid "" msgstr "" "Переключиться на прямолинейный (rectilinear) шаблон?\n" "Да - переключиться на прямолинейный шаблон\n" -"Нет - сбросить плотность заполнения до значения по умолчанию (отличного от " -"100%)" +"Нет - сбросить плотность заполнения до значения по умолчанию (отличного от 100%)" msgid "" "While printing by Object, the extruder may collide skirt.\n" @@ -2700,40 +2494,6 @@ msgstr "Пауза при неисправности температуры со msgid "Paused due to heat bed temperature malfunction" msgstr "Пауза при неисправности температуры стола" -msgid "Filament unloading" -msgstr "Выгрузка прутка" - -# ??? Пауза при пропуске шагов -msgid "Skip step pause" -msgstr "Пропуск команды паузы" - -msgid "Filament loading" -msgstr "Загрузка прутка" - -msgid "Motor noise calibration" -msgstr "Калибровка шума двигателя" - -msgid "Paused due to AMS lost" -msgstr "Печать приостановлена из-за потери связи с АСПП" - -msgid "Paused due to low speed of the heat break fan" -msgstr "" -"Печать приостановлена из-за низкой скорости вращения вентилятора головы" - -msgid "Paused due to chamber temperature control error" -msgstr "Печать приостановлена из-за ошибки контроля температуры в камере" - -# ??? или Снижение температуры -msgid "Cooling chamber" -msgstr "Охлаждение камеры" - -msgid "Paused by the Gcode inserted by user" -msgstr "Печать приостановлена G-кодом, вставленным пользователем" - -# Демонстрация шума двигателяы -msgid "Motor noise showoff" -msgstr "Результат калибровки шума двигателя" - msgid "MC" msgstr "Плата управления" @@ -2770,48 +2530,11 @@ msgstr "Ошибка идентификации." msgid "Update failed." msgstr "Сбой обновления." -msgid "" -"The current chamber temperature or the target chamber temperature exceeds " -"45℃.In order to avoid extruder clogging,low temperature filament(PLA/PETG/" -"TPU) is not allowed to be loaded." -msgstr "" -"Текущая температура в камере или целевая температура в камере превышает 45℃. " -"Чтобы избежать засорения экструдера, запрещается загрузка низкотемпературной " -"печатной нити (PLA/PETG/TPU)." - -msgid "" -"Low temperature filament(PLA/PETG/TPU) is loaded in the extruder.In order to " -"avoid extruder clogging,it is not allowed to set the chamber temperature " -"above 45℃." -msgstr "" -"В экструдер загружается низкотемпературная пластиковая нить (PLA/PETG/TPU). " -"Чтобы избежать засорения экструдера, запрещается устанавливать температуру в " -"камере выше 45℃." - -msgid "" -"When you set the chamber temperature below 40℃, the chamber temperature " -"control will not be activated. And the target chamber temperature will " -"automatically be set to 0℃." -msgstr "" -"Если вы установили температура в камере ниже 40℃, то контроль температуры в " -"камере не запустится, а целевая температура в ней будет автоматически " -"установлена на 0℃." - msgid "Failed to start printing job" msgstr "Не удалось запустить задание на печать." -msgid "" -"This calibration does not support the currently selected nozzle diameter" -msgstr "Данная калибровка не поддерживает выбранный диаметр сопла" - -msgid "Current flowrate cali param is invalid" -msgstr "Текущая величина калибровки скорости потока недопустима" - -msgid "Selected diameter and machine diameter do not match" -msgstr "Выбранный диаметр и диаметр профиля принтера не совпадают" - -msgid "Failed to generate cali gcode" -msgstr "Не удалось сгенерировать калибровочный G-код" +msgid "Invalid nozzle diameter" +msgstr "Недопустимый диаметр сопла" msgid "Calibration error" msgstr "Ошибка калибровки" @@ -2822,22 +2545,14 @@ msgstr "Печать TPU с помощью АСПП не поддерживае msgid "Bambu PET-CF/PA6-CF is not supported by AMS." msgstr "Печать Bambu PET-CF/PA6-CF с помощью АСПП не поддерживается." -msgid "" -"Damp PVA will become flexible and get stuck inside AMS,please take care to " -"dry it before use." -msgstr "" -"Влажный PVA становится гибким и застревает внутри АСПП, поэтому перед " -"использованием его необходимо просушить." +msgid "Damp PVA will become flexible and get stuck inside AMS,please take care to dry it before use." +msgstr "Влажный PVA становится гибким и застревает внутри АСПП, поэтому перед использованием его необходимо просушить." -msgid "" -"CF/GF filaments are hard and brittle, It's easy to break or get stuck in " -"AMS, please use with caution." -msgstr "" -"CF/GF пластиковые нити твердые и хрупкие, легко ломаются или застревают в " -"АСПП, поэтому используйте их с осторожностью." +msgid "CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution." +msgstr "CF/GF пластиковые нити твердые и хрупкие, легко ломаются или застревают в АСПП, поэтому используйте их с осторожностью." msgid "default" -msgstr "По умолчанию" +msgstr "По ум." msgid "parameter name" msgstr "Имя параметра" @@ -2912,6 +2627,9 @@ msgstr "Поток: " msgid "Layer Time: " msgstr "Время печати слоя: " +msgid "Fan Speed: " +msgstr "Скорость вентилятора: " + msgid "Fan: " msgstr "Скорость вентилятора: " @@ -2939,8 +2657,8 @@ msgstr "Очищено" msgid "Total" msgstr "Общее" -msgid "Total Estimation" -msgstr "" +msgid "Total Time Estimation" +msgstr "Оценка общего времени" msgid "Total time" msgstr "Общее время печати" @@ -3143,9 +2861,6 @@ msgstr "Разрешить использование нескольких ма msgid "Avoid extrusion calibration region" msgstr "Избегать зону калибровки экструзии" -msgid "Align to Y axis" -msgstr "Выровнять по оси Y" - msgid "Add" msgstr "Добавить" @@ -3204,12 +2919,8 @@ msgid "Size:" msgstr "Размер:" #, c-format, boost-format -msgid "" -"Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please " -"separate the conflicted objects farther (%s <-> %s)." -msgstr "" -"В G-коде на %d слое (z = %.2lf мм) обнаружен конфликт путей. Пожалуйста, " -"разместите конфликтующие модели дальше друг от друга (%s <-> %s)." +msgid "Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please separate the conflicted objects farther (%s <-> %s)." +msgstr "В G-коде на %d слое (z = %.2lf мм) обнаружен конфликт путей. Пожалуйста, разместите конфликтующие модели дальше друг от друга (%s <-> %s)." msgid "An object is layed over the boundary of plate." msgstr "Модель выходить за границы печатного стола." @@ -3225,13 +2936,10 @@ msgstr "При редактировании, те модели с которым msgid "" "An object is laid over the boundary of plate or exceeds the height limit.\n" -"Please solve the problem by moving it totally on or off the plate, and " -"confirming that the height is within the build volume." +"Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume." msgstr "" "Модель выходит за границы стола или превышает высоту печати.\n" -"Пожалуйста, устраните проблему, уместив всю модель в границы стола (или за " -"пределы стола) и убедитесь, что высота модели находится в пределах область " -"построения." +"Пожалуйста, устраните проблему, уместив всю модель в границы стола (или за пределы стола) и убедитесь, что высота модели находится в пределах область построения." msgid "Calibration step selection" msgstr "Выбор шага калибровки" @@ -3242,22 +2950,17 @@ msgstr "Калибровка микролидаром" msgid "Bed leveling" msgstr "Выравнивание стола" -msgid "Vibration compensation" -msgstr "Компенсация вибрации" - -msgid "Motor noise cancellation" -msgstr "Шумоподавление двигателя" +msgid "Resonance frequency identification" +msgstr "Идентификация резонансной частоты" msgid "Calibration program" msgstr "Программа калибровки" msgid "" -"The calibration program detects the status of your device automatically to " -"minimize deviation.\n" +"The calibration program detects the status of your device automatically to minimize deviation.\n" "It keeps the device performing optimally." msgstr "" -"Программа калибровки автоматически определяет состояние вашего принтера, " -"чтобы свести к минимуму ошибки оборудования.\n" +"Программа калибровки автоматически определяет состояние вашего принтера, чтобы свести к минимуму ошибки оборудования.\n" "Это обеспечивает оптимальную работу устройства." msgid "Calibration Flow" @@ -3266,9 +2969,6 @@ msgstr "Калибровка потока" msgid "Start Calibration" msgstr "Запустить калибровку" -msgid "No step selected" -msgstr "Шаг не задан" - msgid "Completed" msgstr "Завершено" @@ -3302,9 +3002,7 @@ msgstr "Пожалуйста, введите код доступа к принт msgid "" "You can find it in \"Settings > Network > Connection code\"\n" "on the printer, as shown in the figure:" -msgstr "" -"Вы можете найти его на принтере в разделе Настройки > Сеть > Код " -"подключения, как показано на рисунке:" +msgstr "Вы можете найти его на принтере в разделе Настройки > Сеть > Код подключения, как показано на рисунке:" msgid "Invalid input." msgstr "Неверный ввод." @@ -3493,7 +3191,7 @@ msgid "Export all objects as STL" msgstr "Экспортировать все модели в STL" msgid "Export Generic 3MF" -msgstr "Экспортировать в общий 3MF" +msgstr "Экспорт в общий 3MF" msgid "Export 3mf file without using some 3mf-extensions" msgstr "Экспорт в 3mf без использования 3mf-расширений" @@ -3502,16 +3200,16 @@ msgid "Export current sliced file" msgstr "Экспортировать текущий нарезанный файл" msgid "Export all plate sliced file" -msgstr "Экспортировать все нарезанные столы в файл" +msgstr "Экспорт всех нарезанных столов в файл" msgid "Export G-code" -msgstr "Экспортировать в G-код" +msgstr "Экспорт в G-код" msgid "Export current plate as G-code" msgstr "Экспортировать текущие модели со стола в G-код" msgid "Export &Configs" -msgstr "Экспортировать конфигурации" +msgstr "Экспорт конфигурации" msgid "Export current configuration to files" msgstr "Экспортировать текущую конфигурацию в файл" @@ -3589,7 +3287,7 @@ msgid "Show &Overhang" msgstr "Показать &нависания" msgid "Show object overhang highlight in 3D scene" -msgstr "Подсвечивать нависания у модели в 3D сцене" +msgstr "Подсвечивать нависания у модели в 3D-сцене" msgid "Preferences" msgstr "Параметры" @@ -3720,8 +3418,7 @@ msgstr "Выберите профиль для загрузки:" #, c-format, boost-format msgid "There is %d config imported. (Only non-system and compatible configs)" -msgid_plural "" -"There are %d configs imported. (Only non-system and compatible configs)" +msgid_plural "There are %d configs imported. (Only non-system and compatible configs)" msgstr[0] "Импортирована %d конфигурация (только не системная и совместимая)." msgstr[1] "Импортировано %d конфигурации (только не системные и совместимые)." msgstr[2] "Импортировано %d конфигураций (только не системные и совместимые)." @@ -3769,6 +3466,9 @@ msgstr "Принтер занят загрузкой. Дождитесь зав msgid "Loading..." msgstr "Загрузка..." +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "Ошибка инициализации (не поддерживается в режиме «Только LAN»)!" + msgid "Initialize failed (Not supported on the current printer version)!" msgstr "Ошибка инициализации (не поддерживается в текущей версии принтера)!" @@ -3778,6 +3478,9 @@ msgstr "Ошибка инициализации (Недоступно в реж msgid "Initialize failed (Missing LAN ip of printer)!" msgstr "Ошибка инициализации (отсутствует IP-адрес принтера в локальной сети)!" +msgid "Initialize failed (Not supported by printer)!" +msgstr "Ошибка инициализации (не поддерживается принтером)!" + msgid "Initializing..." msgstr "Инициализация..." @@ -3796,9 +3499,7 @@ msgid "Stopped." msgstr "Остановлено." msgid "LAN Connection Failed (Failed to start liveview)" -msgstr "" -"Сбой подключения к локальной сети (не удалось запустить просмотр в реальном " -"времени)" +msgstr "Сбой подключения к локальной сети (не удалось запустить просмотр в реальном времени)" msgid "" "Virtual Camera Tools is required for this task!\n" @@ -3812,11 +3513,11 @@ msgstr "Загрузка Virtual Camera Tools" msgid "" "Another virtual camera is running.\n" -"Orca Slicer supports only a single virtual camera.\n" +"Bambu Studio supports only a single virtual camera.\n" "Do you want to stop this virtual camera?" msgstr "" "Уже работает одна виртуальная камера.\n" -"Orca Slicer поддерживает только одну виртуальную камеру.\n" +"Bambu Studio поддерживает только одну виртуальную камеру.\n" "Хотите остановить эту виртуальную камеру?" #, c-format, boost-format @@ -3884,6 +3585,9 @@ msgstr "Пакетное управление файлами." msgid "No printers." msgstr "Принтеры отсутствуют." +msgid "Not supported by this model of printer!" +msgstr "Не поддерживается этой моделью принтера!" + #, c-format, boost-format msgid "Connect failed [%d]!" msgstr "Ошибка подключения [%d]!" @@ -3891,6 +3595,19 @@ msgstr "Ошибка подключения [%d]!" msgid "Loading file list..." msgstr "Загрузка списка файлов..." +msgid "No files" +msgstr "Файлы отсутствуют" + +msgid "Not accessible in LAN-only mode!" +msgstr "Недоступно в режиме «Только LAN»!" + +msgid "Missing LAN ip of printer!" +msgstr "Отсутствует сетевой адрес принтера!" + +#, c-format, boost-format +msgid "You are going to delete %u files. Are you sure to continue?" +msgstr "Вы собираетесь удалить файлы: %u шт. Вы уверены, что хотите это сделать?" + #, c-format, boost-format msgid "No files [%d]" msgstr "Файлы отсутствуют [%d]" @@ -3901,17 +3618,10 @@ msgstr "Ошибка загрузки [%d]" #, c-format, boost-format msgid "You are going to delete %u file from printer. Are you sure to continue?" -msgid_plural "" -"You are going to delete %u files from printer. Are you sure to continue?" -msgstr[0] "" -"Вы собираетесь удалить %u файл с принтера. Вы уверены, что хотите это " -"сделать?" -msgstr[1] "" -"Вы собираетесь удалить %u файла с принтера. Вы уверены, что хотите это " -"сделать?" -msgstr[2] "" -"Вы собираетесь удалить %u файлов с принтера. Вы уверены, что хотите это " -"сделать?" +msgid_plural "You are going to delete %u files from printer. Are you sure to continue?" +msgstr[0] "Вы собираетесь удалить %u файл с принтера. Вы уверены, что хотите это сделать?" +msgstr[1] "Вы собираетесь удалить %u файла с принтера. Вы уверены, что хотите это сделать?" +msgstr[2] "Вы собираетесь удалить %u файлов с принтера. Вы уверены, что хотите это сделать?" msgid "Delete files" msgstr "Удалить файлы" @@ -3932,12 +3642,8 @@ msgstr "Не удалось получить информацию о модел msgid "Failed to parse model infomations." msgstr "Не удалось проанализировать информацию о модели." -msgid "" -"The .gcode.3mf file contains no G-code data.Please slice it with Orca Slicer " -"and export a new .gcode.3mf file." -msgstr "" -"Файл .gcode.3mf не содержит G-кода. Пожалуйста, нарежьте его в программе " -"Orca Slicer и экспортируйте новый файл .gcode.3mf." +msgid "The .gcode.3mf file contains no G-code data.Please slice it whthBambu Studio and export a new .gcode.3mf file." +msgstr "Файл .gcode.3mf не содержит G-кода. Пожалуйста, нарежьте его в программе Bambu Studio и экспортируйте новый файл .gcode.3mf." #, c-format, boost-format msgid "File '%s' was lost! Please download it again." @@ -4022,27 +3728,12 @@ msgstr "0" msgid "Layer: N/A" msgstr "Слой: Н/Д" +msgid "Immediately score" +msgstr "Оценить сейчас" + msgid "Clear" msgstr "Очистить" -msgid "" -"You have completed printing the mall model, \n" -"but the synchronization of rating information has failed." -msgstr "" -"Вы завершили печать модели торгового центра, \n" -"но не удалось синхронизировать информацию о рейтинге." - -msgid "How do you like this printing file?" -msgstr "На сколько вы оцениваете этот напечатанный файл?" - -msgid "" -"(The model has already been rated. Your rating will overwrite the previous " -"rating.)" -msgstr "(Модели уже присвоен рейтинг. Ваш рейтинг перезапишет предыдущий.)" - -msgid "Rate" -msgstr "Оценка" - msgid "Camera" msgstr "Камера" @@ -4091,6 +3782,17 @@ msgstr "Отмена печати" msgid "Are you sure you want to cancel this print?" msgstr "Вы уверены, что хотите отменить эту печать?" +#, c-format, boost-format +msgid "Disconnected from printer [%s] due to LAN mode disabled.Please reconnect the printer by logging in with your user account." +msgstr "Соединение с принтером [%s] разорвано из-за отключения режима «Только LAN». Повторно подключитесь к принтеру, войдя в свою учётную запись." + +#, c-format, boost-format +msgid "Disconnected from printer [%s] due to LAN mode enabled.Please reconnect the printer by inputting Access Code which can be gotten from printer screen." +msgstr "Соединение с принтером [%s] разорвано из-за включения режима «Только LAN». Повторно подключитесь к принтеру, введя код доступа, который можно получить на экране принтера." + +msgid "Done" +msgstr "Готово" + msgid "Downloading..." msgstr "Загрузка..." @@ -4105,14 +3807,18 @@ msgstr "Количество заданий в очереди на облачн msgid "Layer: %s" msgstr "Слой: %s" +msgid "Please give a score for your favorite Bambu Market model." +msgstr "Пожалуйста, поставьте оценку вашей любимой модели в магазине Bambu Market." + +msgid "Score" +msgstr "Рейтинг" + #, c-format, boost-format msgid "Layer: %d/%d" msgstr "Слой: %d/%d" msgid "Please heat the nozzle to above 170 degree before loading filament." -msgstr "" -"Пожалуйста, перед загрузкой нити, нагрейте сопло до температуры выше 170 " -"градусов." +msgstr "Пожалуйста, перед загрузкой нити, нагрейте сопло до температуры выше 170 градусов." msgid "Still unload" msgstr "Ещё выгружается" @@ -4123,12 +3829,8 @@ msgstr "Ещё загружается" msgid "Please select an AMS slot before calibration" msgstr "Пожалуйста, выберите слот АСПП перед калибровкой" -msgid "" -"Cannot read filament info: the filament is loaded to the tool head,please " -"unload the filament and try again." -msgstr "" -"Не удаётся считать информацию о пластиковой нити. Пластиковая нить загружена " -"в голову, пожалуйста, выгрузите её и повторите попытку." +msgid "Cannot read filament info: the filament is loaded to the tool head,please unload the filament and try again." +msgstr "Не удаётся считать информацию о пластиковой нити. Пластиковая нить загружена в голову, пожалуйста, выгрузите её и повторите попытку." msgid "This only takes effect during printing" msgstr "Применимо только во время печати" @@ -4148,115 +3850,6 @@ msgstr "Сумасшедший" msgid "Can't start this without SD card." msgstr "Невозможно запустить без SD-карты." -msgid "Rate the Print Profile" -msgstr "Оценить профиль печати" - -msgid "Comment" -msgstr "Комментарий" - -msgid "Rate this print" -msgstr "Оценить эту печать" - -msgid "Add Photo" -msgstr "Добавить фото" - -msgid "Delete Photo" -msgstr "Удалить фото" - -msgid "Submit" -msgstr "Отправить" - -msgid "Please click on the star first." -msgstr "Пожалуйста, сначала нажмите на звездочку." - -msgid "InFo" -msgstr "Информация" - -msgid "Get oss config failed." -msgstr "" -"Не удалось получить конфигурацию OSS.\n" -"\n" -"Ошибка получения конфигурации OSS." - -msgid "Upload Pictrues" -msgstr "Отправка изображений" - -msgid "Number of images successfully uploaded" -msgstr "Количество успешно загруженных изображений" - -msgid " upload failed" -msgstr " ошибка отправки" - -msgid " upload config prase failed\n" -msgstr " ошибка обработки конфигурации при отправке\n" - -# ??? -msgid " No corresponding storage bucket\n" -msgstr " Отсутствует хранилище данных\n" - -msgid " can not be opened\n" -msgstr " не удаётся открыть\n" - -msgid "" -"The following issues occurred during the process of uploading images. Do you " -"want to ignore them?\n" -"\n" -msgstr "" -"В процессе загрузки изображений возникли следующие проблемы. Игнорировать " -"их?\n" -"\n" - -msgid "info" -msgstr "Информация" - -msgid "Synchronizing the printing results. Please retry a few seconds later." -msgstr "" -"Синхронизация результатов печати. Повторите попытку через несколько секунд." - -msgid "Upload failed\n" -msgstr "Ошибка отправки\n" - -msgid "obtaining instance_id failed\n" -msgstr "не удалось получить instance_id\n" - -msgid "" -"Your comment result cannot be uploaded due to some reasons. As follows:\n" -"\n" -" error code: " -msgstr "" -"Ваш комментарий не может быть отправлен по некоторым причинам. Причины:\n" -"\n" -"Код ошибки: " - -msgid "error message: " -msgstr "сообщение об ошибке: " - -msgid "" -"\n" -"\n" -"Would you like to redirect to the webpage for rating?" -msgstr "" -"\n" -"\n" -"Хотите перейти на страницу для выставления оценки?" - -msgid "" -"Some of your images failed to upload. Would you like to redirect to the " -"webpage for rating?" -msgstr "" -"Некоторые из ваших изображений не удалось загрузить. Хотите перейти на " -"страницу для выставления оценки?" - -msgid "You can select up to 16 images." -msgstr "Допускается выбор до 16 изображений." - -msgid "" -"At least one successful print record of this print profile is required \n" -"to give a positive rating(4 or 5stars)." -msgstr "" -"Для выставления положительной оценки (4 или 5 звезд) требуется хотя бы одна " -"успешная запись о печати данным профилем печати." - msgid "Status" msgstr "Статус" @@ -4386,9 +3979,6 @@ msgstr "Предупреждение:" msgid "Export successfully." msgstr "Успешно экспортировано." -msgid "Model file downloaded." -msgstr "Файл модели скачан." - msgid "Serious warning:" msgstr "Серьезное предупреждение:" @@ -4422,11 +4012,8 @@ msgstr "Слои" msgid "Range" msgstr "Диапазон" -msgid "" -"The application cannot run normally because OpenGL version is lower than " -"2.0.\n" -msgstr "" -"Приложение не может работать нормально, так как версия OpenGL ниже 2.0.\n" +msgid "The application cannot run normally because OpenGL version is lower than 2.0.\n" +msgstr "Приложение не может работать нормально, так как версия OpenGL ниже 2.0.\n" msgid "Please upgrade your graphics card driver." msgstr "Пожалуйста, обновите драйвер вашей видеокарты." @@ -4462,12 +4049,8 @@ msgstr "Чувствительность" msgid "Enable detection of build plate position" msgstr "Определение положения печатной пластины" -msgid "" -"The localization tag of build plate is detected, and printing is paused if " -"the tag is not in predefined range." -msgstr "" -"Функция обнаружения метки (QR-кода) печатной пластины. Печать " -"приостанавливается, если метка находится не в том месте." +msgid "The localization tag of build plate is detected, and printing is paused if the tag is not in predefined range." +msgstr "Функция обнаружения метки (QR-кода) печатной пластины. Печать приостанавливается, если метка находится не в том месте." msgid "First Layer Inspection" msgstr "Проверка первого слоя" @@ -4475,10 +4058,6 @@ msgstr "Проверка первого слоя" msgid "Auto-recovery from step loss" msgstr "Автовосстановление после потери шагов" -# оповещения, подсказки??? -msgid "Allow Prompt Sound" -msgstr "Разрешить звуковые уведомления" - msgid "Global" msgstr "Общие" @@ -4500,24 +4079,6 @@ msgstr "Настройки прутка" msgid "Printer settings" msgstr "Настройки принтера" -msgid "Remove current plate (if not last one)" -msgstr "Удалить текущую печатную пластину (кроме последней)" - -msgid "Auto orient objects on current plate" -msgstr "Автоориентация моделей на текущей печатной пластине" - -msgid "Arrange objects on current plate" -msgstr "Расставить модели на текущей печатной пластине" - -msgid "Unlock current plate" -msgstr "Разблокировать текущую печатную пластину" - -msgid "Lock current plate" -msgstr "Заблокировать текущую печатную пластину" - -msgid "Customize current plate" -msgstr "Настроить текущую печатную пластину" - msgid "Untitled" msgstr "Без названия" @@ -4573,8 +4134,7 @@ msgstr "Синхронизировать список материалов из msgid "Set filaments to use" msgstr "Выбор пластиковой нити" -msgid "" -"No AMS filaments. Please select a printer in 'Device' page to load AMS info." +msgid "No AMS filaments. Please select a printer in 'Device' page to load AMS info." msgstr "" "АСПП недоступна. Пожалуйста, выберите принтер \n" "на странице «Принтер», чтобы загрузить информацию о АСПП." @@ -4582,19 +4142,11 @@ msgstr "" msgid "Sync filaments with AMS" msgstr "Синхронизация прутка с АСПП" -msgid "" -"Sync filaments with AMS will drop all current selected filament presets and " -"colors. Do you want to continue?" -msgstr "" -"При синхронизации пластиковых нитей с АСПП все текущие выбранные профили " -"прутков и цвета будут сброшены. Продолжить?" +msgid "Sync filaments with AMS will drop all current selected filament presets and colors. Do you want to continue?" +msgstr "При синхронизации пластиковых нитей с АСПП все текущие выбранные профили прутков и цвета будут сброшены. Продолжить?" -msgid "" -"Already did a synchronization, do you want to sync only changes or resync " -"all?" -msgstr "" -"Синхронизация уже выполнена. Хотите синхронизировать только изменения или " -"заново синхронизировать всё?" +msgid "Already did a synchronization, do you want to sync only changes or resync all?" +msgstr "Синхронизация уже выполнена. Хотите синхронизировать только изменения или заново синхронизировать всё?" msgid "Sync" msgstr "Только изменения" @@ -4603,29 +4155,18 @@ msgid "Resync" msgstr "Всё" msgid "There are no compatible filaments, and sync is not performed." -msgstr "" -"Синхронизация не выполнена, ввиду отсутствия совместимых пластиковых нитей." +msgstr "Синхронизация не выполнена, ввиду отсутствия совместимых пластиковых нитей." -msgid "" -"There are some unknown filaments mapped to generic preset. Please update " -"Orca Slicer or restart Orca Slicer to check if there is an update to system " -"presets." -msgstr "" -"Имеются несколько неизвестных материалов, сопоставленных с общим профилем. " -"Обновите или перезапустите Orca Slicer, чтобы проверить наличие обновлений " -"системных профилей." +msgid "There are some unknown filaments mapped to generic preset. Please update Orca Slicer or restart Orca Slicer to check if there is an update to system presets." +msgstr "Имеются несколько неизвестных материалов, сопоставленных с общим профилем. Обновите или перезапустите Orca Slicer, чтобы проверить наличие обновлений системных профилей." #, boost-format msgid "Do you want to save changes to \"%1%\"?" msgstr "Вы хотите сохранить изменения в \"%1%\"?" #, c-format, boost-format -msgid "" -"Successfully unmounted. The device %s(%s) can now be safely removed from the " -"computer." -msgstr "" -"Размонтирование прошло успешно. Теперь устройство %s(%s) может быть " -"безопасно извлечено из компьютера." +msgid "Successfully unmounted. The device %s(%s) can now be safely removed from the computer." +msgstr "Размонтирование прошло успешно. Теперь устройство %s(%s) может быть безопасно извлечено из компьютера." #, c-format, boost-format msgid "Ejecting of device %s(%s) has failed." @@ -4637,38 +4178,21 @@ msgstr "Обнаружен предыдущий несохраненный пр msgid "Restore" msgstr "Восстановить" -msgid "" -"The current hot bed temperature is relatively high. The nozzle may be " -"clogged when printing this filament in a closed enclosure. Please open the " -"front door and/or remove the upper glass." -msgstr "" -"Текущая температура стола довольно высока. При печати этим материалом в " -"закрытом корпусе возможно засорение сопла. Откройте переднюю дверцу и/или " -"верхнюю крышку принтера." +msgid "The bed temperature exceeds filament's vitrification temperature. Please open the front door of printer before printing to avoid nozzle clog." +msgstr "Температура стола превышает температуру стеклования пластиковой нити. Пожалуйста, откройте переднюю дверцу принтера перед печатью, чтобы избежать засорения сопла." -msgid "" -"The nozzle hardness required by the filament is higher than the default " -"nozzle hardness of the printer. Please replace the hardened nozzle or " -"filament, otherwise, the nozzle will be attrited or damaged." -msgstr "" -"Твердость сопла, установленного по умолчанию, не достаточна для печати " -"данной пластиковой нитью. Замените сопло на закаленное или смените " -"пластиковую нить. В противном случае сопло будет изношено или повреждено." - -msgid "" -"Enabling traditional timelapse photography may cause surface imperfections. " -"It is recommended to change to smooth mode." -msgstr "" -"Включение обычного режима таймлапса может привести к появлению дефектов " -"поверхности, поэтому рекомендуется изменить режим на плавный." +msgid "The nozzle hardness required by the filament is higher than the default nozzle hardness of the printer. Please replace the hardened nozzle or filament, otherwise, the nozzle will be attrited or damaged." +msgstr "Твердость сопла, установленного по умолчанию, не достаточна для печати данной пластиковой нитью. Замените сопло на закаленное или смените пластиковую нить. В противном случае сопло будет изношено или повреждено." #, c-format, boost-format msgid "Loading file: %s" msgstr "Загрузка файла: %s" msgid "The 3mf is not supported by OrcaSlicer, load geometry data only." -msgstr "" -"Этот 3mf создан не в OrcaSlicer, поэтому загрузятся только данные геометрии." +msgstr "Этот 3mf создан не в OrcaSlicer, поэтому загрузятся только данные геометрии." + +msgid "The 3mf is not from Bambu Lab, load geometry data only." +msgstr "Этот 3mf создан не в Bambu Lab, поэтому загрузятся только данные геометрии." msgid "Load 3mf" msgstr "Загрузка 3mf" @@ -4677,14 +4201,9 @@ msgid "The Config can not be loaded." msgstr "Конфигурация не может быть загружена." msgid "The 3mf is generated by old Orca Slicer, load geometry data only." -msgstr "" -"Этот 3mf создан в старой версии Orca Slicer, поэтому загрузятся только " -"данные геометрии." +msgstr "Этот 3mf создан в старой версии Orca Slicer, поэтому загрузятся только данные геометрии." -#, c-format, boost-format -msgid "" -"The 3mf's version %s is newer than %s's version %s, Found following keys " -"unrecognized:" +msgid "The 3mf's version %s is newer than %s's version %s, Found following keys unrecognized:" msgstr "" "Версия этого формата 3mf (%s) новее текущей версии %s (%s). \n" "Обнаружены следующие нераспознанные ключи:" @@ -4696,9 +4215,7 @@ msgid "Newer 3mf version" msgstr "Новая версия 3mf" #, c-format, boost-format -msgid "" -"The 3mf's version %s is newer than %s's version %s, Suggest to upgrade your " -"software." +msgid "The 3mf's version %s is newer than %s's version %s, Suggest to upgrade your software." msgstr "" "Версия этого формата 3mf (%s) новее текущей версии %s (%s). \n" "Рекомендуется обновить программу." @@ -4723,8 +4240,7 @@ msgstr "В названии могут присутствовать ненужн #, boost-format msgid "Failed loading file \"%1%\". An invalid configuration was found." -msgstr "" -"Не удалось загрузить файл \"%1%\". Обнаружена недопустимая конфигурация." +msgstr "Не удалось загрузить файл \"%1%\". Обнаружена недопустимая конфигурация." msgid "Objects with zero volume removed" msgstr "Модели с нулевым объёмом удалены" @@ -4757,8 +4273,7 @@ msgid "Multi-part object detected" msgstr "Обнаружена модель, состоящая из нескольких частей" msgid "Load these files as a single object with multiple parts?\n" -msgstr "" -"Загрузить эти файлы как единую модель состоящую из нескольких частей?\n" +msgstr "Загрузить эти файлы как единую модель состоящую из нескольких частей?\n" msgid "Object with multiple parts was detected" msgstr "Обнаружена модель, состоящая из нескольких частей" @@ -4766,9 +4281,7 @@ msgstr "Обнаружена модель, состоящая из нескол msgid "The file does not contain any geometry data." msgstr "Файл не содержит никаких геометрических данных." -msgid "" -"Your object appears to be too large, Do you want to scale it down to fit the " -"heat bed automatically?" +msgid "Your object appears to be too large, Do you want to scale it down to fit the heat bed automatically?" msgstr "" "Похоже, ваша модель слишком большая. \n" "Хотите автоматически уменьшить её масштаб, \n" @@ -4807,39 +4320,18 @@ msgstr "Выбранная модель не может быть разделе msgid "Another export job is running." msgstr "Уже идёт другой процесс экспорта." -msgid "Replace from:" -msgstr "Заменить из:" - -msgid "Unable to replace with more than one volume" -msgstr "Невозможно заменить более чем одним объём" - -msgid "Error during replace" -msgstr "Ошибка при выполнении замены" - msgid "Select a new file" msgstr "Выберите новый файл" msgid "File for the replace wasn't selected" msgstr "Файл для замены не выбран" +msgid "Error during replace" +msgstr "Ошибка при выполнении замены" + msgid "Please select a file" msgstr "Пожалуйста, выберите файл" -msgid "Do you want to replace it" -msgstr "Хотите заменить его" - -msgid "Message" -msgstr "Сообщение" - -msgid "Reload from:" -msgstr "Перезагрузка из:" - -msgid "Unable to reload:" -msgstr "Не удалось перезагрузить:" - -msgid "Error during reload" -msgstr "Ошибка во время перезагрузки" - msgid "Slicing" msgstr "Нарезка" @@ -4862,8 +4354,7 @@ msgstr "Нарезка стола %d" msgid "Please resolve the slicing errors and publish again." msgstr "Пожалуйста, устраните ошибки нарезки и попробуйте опубликовать снова." -msgid "" -"Network Plug-in is not detected. Network related features are unavailable." +msgid "Network Plug-in is not detected. Network related features are unavailable." msgstr "Сетевой плагин не обнаружен. Функции, связанные с сетью, недоступны." msgid "" @@ -4871,8 +4362,7 @@ msgid "" "The loaded file contains gcode only, Can not enter the Prepare page" msgstr "" "Режим только предпросмотра:\n" -"Загруженный файл содержит только G-код, поэтому переход на страницу " -"«Подготовка» невозможен." +"Загруженный файл содержит только G-код, поэтому переход на страницу «Подготовка» невозможен." msgid "You can keep the modified presets to the new project or discard them" msgstr "Изменённые профили можно сохранить в новом проекте или отказаться." @@ -4885,12 +4375,10 @@ msgstr "Загрузить проект" msgid "" "Failed to save the project.\n" -"Please check whether the folder exists online or if other programs open the " -"project file." +"Please check whether the folder exists online or if other programs open the project file." msgstr "" "Не удалось сохранить проект.\n" -"Убедитесь, что папка существует и что файл проекта не открыт в другой " -"программе." +"Убедитесь, что папка существует и что файл проекта не открыт в другой программе." msgid "Save project" msgstr "Сохранение проекта" @@ -4908,13 +4396,6 @@ msgstr "скачивание проекта..." msgid "Project downloaded %d%%" msgstr "Проект загружен %d%%" -msgid "" -"Importing to Orca Slicer failed. Please download the file and manually " -"import it." -msgstr "" -"Не удалось импортировать в Orca Slicer. Загрузите файл и импортируйте его " -"вручную." - msgid "The selected file" msgstr "В выбранном файле" @@ -4971,30 +4452,15 @@ msgstr "Количество копий выбранной модели" msgid "Save G-code file as:" msgstr "Сохранить файл G-кода как:" -msgid "Save SLA file as:" -msgstr "Сохранить SLA файл как:" - -msgid "The provided file name is not valid." -msgstr "Указано недопустимое имя файла." - -msgid "The following characters are not allowed by a FAT file system:" -msgstr "Следующие символы не разрешены файловой системой FAT:" - msgid "Save Sliced file as:" msgstr "Сохранить нарезанный файл как:" #, c-format, boost-format -msgid "" -"The file %s has been sent to the printer's storage space and can be viewed " -"on the printer." +msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer." msgstr "Файл %s отправлен в память принтера и может быть просмотрен на нём." -msgid "" -"Unable to perform boolean operation on model meshes. Only positive parts " -"will be exported." -msgstr "" -"Невозможно выполнить булевы операции над сетками модели. Будут " -"экспортированы только положительные части." +msgid "Unable to perform boolean operation on model meshes. Only positive parts will be exported." +msgstr "Невозможно выполнить булевы операции над сетками модели. Будут экспортированы только положительные части." msgid "Is the printer ready? Is the print sheet in place, empty and clean?" msgstr "Готов ли 3D-принтер? Печатная пластина на месте, пустая и чистая?" @@ -5007,8 +4473,7 @@ msgid "" "Suggest to use auto-arrange to avoid collisions when printing." msgstr "" "Печать по очереди: \n" -"Рекомендуется использовать автоматическую расстановку, чтобы избежать " -"столкновений при печати." +"Рекомендуется использовать автоматическую расстановку, чтобы избежать столкновений при печати." msgid "Send G-code" msgstr "Отправить G-код" @@ -5066,23 +4531,12 @@ msgstr "Треугольников: %1%\n" msgid "Tips:" msgstr "Подсказки:" -msgid "" -"\"Fix Model\" feature is currently only on Windows. Please repair the model " -"on Orca Slicer(windows) or CAD softwares." -msgstr "" -"Функция «Починить модель» в настоящее время доступна только в Windows. " -"Пожалуйста, почините модель с помощью Bambu Studio (Windows) или другой CAD " -"программой." +msgid "\"Fix Model\" feature is currently only on Windows. Please repair the model on Orca Slicer(windows) or CAD softwares." +msgstr "Функция «Починить модель» в настоящее время доступна только в Windows. Пожалуйста, почините модель с помощью Bambu Studio (Windows) или другой CAD программой." #, c-format, boost-format -msgid "" -"Plate% d: %s is not suggested to be used to print filament %s(%s). If you " -"still want to do this printing, please set this filament's bed temperature " -"to non zero." -msgstr "" -"Не рекомендуется использовать печатную пластину% d (%s) для печати прутком " -"%s (%s). Если вы всё же хотите сделать это, то установите температуру стола " -"для этого прутка на ненулевое значение." +msgid "Plate% d: %s is not suggested to be used to print filament %s(%s). If you still want to do this printing, please set this filament's bed temperature to non zero." +msgstr "Не рекомендуется использовать печатную пластину% d (%s) для печати прутком %s (%s). Если вы всё же хотите сделать это, то установите температуру стола для этого прутка на ненулевое значение." msgid "Switching the language requires application restart.\n" msgstr "Для смены языка требуется перезапуск приложения.\n" @@ -5136,7 +4590,7 @@ msgid "Login Region" msgstr "Регион входа" msgid "Stealth Mode" -msgstr "Режим конфиденциальности (отключение телеметрии Bambulab)." +msgstr "Режим конфиденциальности (отключение телеметрии Bambulab)" msgid "Metric" msgstr "Метрическая СИ" @@ -5149,46 +4603,17 @@ msgstr "" "Единицы \n" "измерения" -msgid "Home" -msgstr "" - -msgid "Default Page" -msgstr "" - -msgid "Set the page opened on startup." -msgstr "" - msgid "Zoom to mouse position" msgstr "Приближать к положению курсор" -msgid "" -"Zoom in towards the mouse pointer's position in the 3D view, rather than the " -"2D window center." -msgstr "" -"Увеличивать масштаб по направлению к курсору в 3D-виде, а не к центру 2D-" -"окна." - -msgid "Use free camera" -msgstr "Использовать свободную камеру" - -msgid "If enabled, use free camera. If not enabled, use constrained camera." -msgstr "" -"Если включено, используется свободное вращение камеры. Если выключено, " -"используется вращение камера с ограничениями." - -msgid "Show splash screen" -msgstr "" - -msgid "Show the splash screen during startup." -msgstr "" +msgid "Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center." +msgstr "Увеличивать масштаб по направлению к курсору в 3D-виде, а не к центру 2D-окна." msgid "Show \"Tip of the day\" notification after start" msgstr "Показывать уведомление с полезным советом при запуске приложения" msgid "If enabled, useful hints are displayed at startup." -msgstr "" -"Если включено, будут показываться уведомления с полезном советом при запуске " -"приложения." +msgstr "Если включено, будут показываться уведомления с полезном советом при запуске приложения." msgid "Show g-code window" msgstr "Показать окно G-кода" @@ -5221,32 +4646,31 @@ msgid "Associate .3mf files to OrcaSlicer" msgstr "Ассоциировать файлы .3mf с OrcaSlicer" msgid "If enabled, sets OrcaSlicer as default application to open .3mf files" -msgstr "" -"Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для " -"открытия .3mf файлов." +msgstr "Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для открытия .3mf файлов." msgid "Associate .stl files to OrcaSlicer" msgstr "Ассоциировать файлы .stl с OrcaSlicer" msgid "If enabled, sets OrcaSlicer as default application to open .stl files" -msgstr "" -"Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для " -"открытия .stl файлов." +msgstr "Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для открытия .stl файлов." msgid "Associate .step/.stp files to OrcaSlicer" msgstr "Ассоциировать файлы .step/.stp с OrcaSlicer" msgid "If enabled, sets OrcaSlicer as default application to open .step files" -msgstr "" -"Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для " -"открытия .step файлов." +msgstr "Если включено, назначает OrcaSlicer в качестве приложения по умолчанию для открытия .step файлов." + +msgid "Online Models" +msgstr "Онлайн-модели" + +msgid "Show online staff-picked models on the home page" +msgstr "Показывать отобранные сотрудниками модели на главной странице" msgid "Maximum recent projects" msgstr "Максимальное количество недавних проектов" msgid "Maximum count of recent projects" -msgstr "" -"Максимальное количество проектов отображаемое в списке недавних проектов." +msgstr "Максимальное количество проектов отображаемое в списке недавних проектов." msgid "Clear my choice on the unsaved projects." msgstr "Очистить мой выбор от несохранённых проектов." @@ -5254,11 +4678,8 @@ msgstr "Очистить мой выбор от несохранённых пр msgid "Auto-Backup" msgstr "Автосоздание резервной копии" -msgid "" -"Backup your project periodically for restoring from the occasional crash." -msgstr "" -"Периодическое создание резервной копии проекта для восстановления после " -"непредвиденного сбоя программы." +msgid "Backup your project periodically for restoring from the occasional crash." +msgstr "Периодическое создание резервной копии проекта для восстановления после непредвиденного сбоя программы." msgid "every" msgstr "каждые" @@ -5338,6 +4759,9 @@ msgstr "ошибка" msgid "warning" msgstr "предупреждение" +msgid "info" +msgstr "Информация" + msgid "debug" msgstr "отладка" @@ -5447,9 +4871,7 @@ msgid "Log Out" msgstr "Выход" msgid "Slice all plate to obtain time and filament estimation" -msgstr "" -"Нарезка всех столов для получения примерного времени печати и расчёта " -"необходимого количества материала." +msgstr "Нарезка всех столов для получения примерного времени печати и расчёта необходимого количества материала." msgid "Packing project data into 3mf file" msgstr "Упаковка данных проекта в файл формата 3mf" @@ -5461,9 +4883,7 @@ msgid "Jump to model publish web page" msgstr "Перейти на веб-страницу публикации модели" msgid "Note: The preparation may takes several minutes. Please be patiant." -msgstr "" -"Примечание: подготовка может занять несколько минут. Пожалуйста, наберитесь " -"терпения." +msgstr "Примечание: подготовка может занять несколько минут. Пожалуйста, наберитесь терпения." msgid "Publish" msgstr "Опубликовать" @@ -5514,8 +4934,7 @@ msgid "Preset \"%1%\" already exists and is incompatible with current printer." msgstr "Профиль \"%1%\" уже существует и несовместим с текущим принтером." msgid "Please note that saving action will replace this preset" -msgstr "" -"Обратите внимание, что при сохранении произойдёт замена текущего профиля." +msgstr "Обратите внимание, что при сохранении произойдёт замена текущего профиля." msgid "The name is not allowed to be empty." msgstr "Имя не может быть пустым." @@ -5559,6 +4978,9 @@ msgstr "Просто переключиться на \"%1%\"" msgid "Task canceled" msgstr "Задание отменено" +msgid "Upload task timed out. Please check the network problem and try again" +msgstr "Истекло время ожидания отправки задания. Проверьте сетевое подключение и повторите попытку." + msgid "(LAN)" msgstr "(LAN)" @@ -5587,23 +5009,11 @@ msgid "Busy" msgstr "Занят" msgid "Bambu Cool Plate" -msgstr "Не нагреваемая пластина Bamabu" +msgstr "Не нагреваемая пластина Bambu" msgid "PLA Plate" msgstr "PLA пластина" -msgid "Bambu Engineering Plate" -msgstr "Инженерная пластина Bamabu" - -msgid "Bambu Smooth PEI Plate" -msgstr "Гладкая PEI пластина Bambu" - -msgid "High temperature Plate" -msgstr "Высокотемпературная пластина" - -msgid "Bambu Textured PEI Plate" -msgstr "Текстурированная PEI пластина Bambu " - msgid "Send print job to" msgstr "Отправка задания на печать" @@ -5620,8 +5030,8 @@ msgstr "" "Калибровка\n" "динамики потока" -msgid "Click here if you can't connect to the printer" -msgstr "Нажмите здесь, если вы не можете подключиться к принтеру" +msgid "Can't connect to the printer" +msgstr "Не удаётся подключиться к принтеру" msgid "send completed" msgstr "отправка завершена" @@ -5633,12 +5043,12 @@ msgid "Check the status of current system services" msgstr "Проверка состояния текущих системных служб" msgid "Printer local connection failed, please try again." -msgstr "" -"Не удалось установить локальное соединение с принтером, попробуйте ещё раз." +msgstr "Не удалось установить локальное соединение с принтером, попробуйте ещё раз." msgid "No login account, only printers in LAN mode are displayed" msgstr "" -"Без входа в учётную запись, отображаются только принтеры в локальной сети." +"Без входа в учётную запись, отображаются только \n" +"принтеры в локальной сети." msgid "Connecting to server" msgstr "Подключение к серверу" @@ -5650,70 +5060,36 @@ msgid "Synchronizing device information time out" msgstr "Время ожидания синхронизации информации об устройстве истекло" msgid "Cannot send the print job when the printer is updating firmware" -msgstr "" -"Невозможно отправить задание на печать, при обновлении прошивки принтера." +msgstr "Невозможно отправить задание на печать, при обновлении прошивки принтера." -msgid "" -"The printer is executing instructions. Please restart printing after it ends" -msgstr "" -"Принтер выполняет инструкции. Пожалуйста, перезапустите печать после их " -"завершения." +msgid "The printer is executing instructions. Please restart printing after it ends" +msgstr "Принтер выполняет инструкции. Пожалуйста, перезапустите печать после их завершения." msgid "The printer is busy on other print job" msgstr "Принтер занят другим заданием" #, c-format, boost-format -msgid "" -"Filament %s exceeds the number of AMS slots. Please update the printer " -"firmware to support AMS slot assignment." -msgstr "" -"Количество пластиковых нитей - %s, что превышает количество слотов АСПП. " -"Обновите прошивку принтера, чтобы получить поддержку функции назначения " -"слотов АСПП." +msgid "Filament %s exceeds the number of AMS slots. Please update the printer firmware to support AMS slot assignment." +msgstr "Количество пластиковых нитей - %s, что превышает количество слотов АСПП. Обновите прошивку принтера, чтобы получить поддержку функции назначения слотов АСПП." -msgid "" -"Filament exceeds the number of AMS slots. Please update the printer firmware " -"to support AMS slot assignment." -msgstr "" -"Количество пластиковых нитей превышает количество слотов АСПП. Обновите " -"прошивку принтера, чтобы получить поддержку функции назначения слотов АСПП." +msgid "Filament exceeds the number of AMS slots. Please update the printer firmware to support AMS slot assignment." +msgstr "Количество пластиковых нитей превышает количество слотов АСПП. Обновите прошивку принтера, чтобы получить поддержку функции назначения слотов АСПП." -msgid "" -"Filaments to AMS slots mappings have been established. You can click a " -"filament above to change its mapping AMS slot" -msgstr "" -"Соответствия между материалами и слотами АСПП были установлены. Вы можете " -"нажать на пластиковую нить выше, чтобы вручную задать для неё нужный слот " -"АСПП." +msgid "Filaments to AMS slots mappings have been established. You can click a filament above to change its mapping AMS slot" +msgstr "Соответствия между материалами и слотами АСПП были установлены. Вы можете нажать на пластиковую нить выше, чтобы вручную задать для неё нужный слот АСПП." -msgid "" -"Please click each filament above to specify its mapping AMS slot before " -"sending the print job" -msgstr "" -"Перед отправкой задания на печать, нажмите на каждую пластиковую нить, чтобы " -"вручную задать для неё нужный слот АСПП." +msgid "Please click each filament above to specify its mapping AMS slot before sending the print job" +msgstr "Перед отправкой задания на печать, нажмите на каждую пластиковую нить, чтобы вручную задать для неё нужный слот АСПП." #, c-format, boost-format -msgid "" -"Filament %s does not match the filament in AMS slot %s. Please update the " -"printer firmware to support AMS slot assignment." -msgstr "" -"Материал %s не соответствует материалу в слоте %s АСПП. Обновите прошивку " -"принтера, чтобы получить поддержку функции назначения слотов АСПП." +msgid "Filament %s does not match the filament in AMS slot %s. Please update the printer firmware to support AMS slot assignment." +msgstr "Материал %s не соответствует материалу в слоте %s АСПП. Обновите прошивку принтера, чтобы получить поддержку функции назначения слотов АСПП." -msgid "" -"Filament does not match the filament in AMS slot. Please update the printer " -"firmware to support AMS slot assignment." -msgstr "" -"Материал не соответствует материалу в слоте АСПП. Обновите прошивку " -"принтера, чтобы получить поддержку функции назначения слотов АСПП." +msgid "Filament does not match the filament in AMS slot. Please update the printer firmware to support AMS slot assignment." +msgstr "Материал не соответствует материалу в слоте АСПП. Обновите прошивку принтера, чтобы получить поддержку функции назначения слотов АСПП." -msgid "" -"The printer firmware only supports sequential mapping of filament => AMS " -"slot." -msgstr "" -"Текущая прошивка принтера поддерживает только последовательное сопоставление " -"пластиковых нитей => слот АСПП." +msgid "The printer firmware only supports sequential mapping of filament => AMS slot." +msgstr "Текущая прошивка принтера поддерживает только последовательное сопоставление пластиковых нитей => слот АСПП." msgid "An SD card needs to be inserted before printing." msgstr "Перед печатью необходимо вставить SD-карту." @@ -5724,12 +5100,8 @@ msgstr "Выбранный принтер несовместим с выбран msgid "An SD card needs to be inserted to record timelapse." msgstr "Для записи таймлапсов необходимо вставить SD-карту." -msgid "" -"Cannot send the print job to a printer whose firmware is required to get " -"updated." -msgstr "" -"Невозможно отправить задание печати на принтер, прошивка которого нуждается " -"в обновлении." +msgid "Cannot send the print job to a printer whose firmware is required to get updated." +msgstr "Невозможно отправить задание печати на принтер, прошивка которого нуждается в обновлении." msgid "Cannot send the print job for empty plate" msgstr "Невозможно отправить задание на печать, так как стол пуст." @@ -5737,54 +5109,27 @@ msgstr "Невозможно отправить задание на печать msgid "This printer does not support printing all plates" msgstr "Данный принтер не поддерживает печать на всех типах печатных пластин." -msgid "" -"When enable spiral vase mode, machines with I3 structure will not generate " -"timelapse videos." -msgstr "" -"При включении режима «Спиральная ваза» принтеры с кинематикой I3 не будут " -"писать таймлапс." - -msgid "" -"When print by object, machines with I3 structure will not generate timelapse " -"videos." -msgstr "" -"При печати по очереди, принтеры с кинематикой I3 не будут писать таймлапс." - msgid "Errors" msgstr "Ошибок" msgid "Please check the following:" msgstr "Пожалуйста, проверьте следующую информацию:" -msgid "" -"The printer type selected when generating G-Code is not consistent with the " -"currently selected printer. It is recommended that you use the same printer " -"type for slicing." -msgstr "" -"Выбранный профиль принтера в настройках слайсера не совпадает с фактическим " -"принтером. Для нарезки рекомендуется использовать тот же профиль принтера." +msgid "The printer type selected when generating G-Code is not consistent with the currently selected printer. It is recommended that you use the same printer type for slicing." +msgstr "Выбранный профиль принтера в настройках слайсера не совпадает с фактическим принтером. Для нарезки рекомендуется использовать тот же профиль принтера." #, c-format, boost-format msgid "%s is not supported by AMS." msgstr "%s не поддерживается АСПП." -msgid "" -"There are some unknown filaments in the AMS mappings. Please check whether " -"they are the required filaments. If they are okay, press \"Confirm\" to " -"start printing." -msgstr "" -"В АСПП установлены неизвестные пластиковые нити. Убедитесь, что стоят именно " -"те, что вам нужны. Если всё в порядке, нажмите «Подтвердить», чтобы начать " -"печать." +msgid "There are some unknown filaments in the AMS mappings. Please check whether they are the required filaments. If they are okay, press \"Confirm\" to start printing." +msgstr "В АСПП установлены неизвестные пластиковые нити. Убедитесь, что стоят именно те, что вам нужны. Если всё в порядке, нажмите «Подтвердить», чтобы начать печать." -msgid "" -"Please click the confirm button if you still want to proceed with printing." +msgid "Please click the confirm button if you still want to proceed with printing." msgstr "Нажмите кнопку подтверждения, если всё ещё хотите продолжить печать." -msgid "" -"Connecting to the printer. Unable to cancel during the connection process." -msgstr "" -"Подключение к принтеру. Невозможно отменить во время процесса подключения." +msgid "Connecting to the printer. Unable to cancel during the connection process." +msgstr "Подключение к принтеру. Невозможно отменить во время процесса подключения." msgid "Preparing print job" msgstr "Подготовка задания на печать" @@ -5795,12 +5140,8 @@ msgstr "Неправильные данные файла печати. Пожа msgid "The name length exceeds the limit." msgstr "Длина имени превышает установленное ограничение." -msgid "" -"Caution to use! Flow calibration on Textured PEI Plate may fail due to the " -"scattered surface." -msgstr "" -"Внимание! Калибровка потока на текстурированной пластине с PEI покрытием " -"может быть неудачной из-за шероховатой поверхности." +msgid "Caution to use! Flow calibration on Textured PEI Plate may fail due to the scattered surface." +msgstr "Внимание! Калибровка потока на текстурированной пластине с PEI покрытием может быть неудачной из-за шероховатой поверхности." msgid "Automatic flow calibration using Micro Lidar" msgstr "Автокалибровка потока с помощью микролидара" @@ -5817,8 +5158,8 @@ msgstr "Во время обновления невозможно отправи msgid "An SD card needs to be inserted before send to printer SD card." msgstr "Перед отправкой на SD-карту, сначала вставьте её в принтер." -msgid "The printer is required to be in the same LAN as Orca Slicer." -msgstr "Принтер должен находиться в одной локальной сети с Orca Slicer." +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "Принтер должен находиться в одной локальной сети с Bambu Studio." msgid "The printer does not support sending to printer SD card." msgstr "Принтер не поддерживает отправку на SD-карту." @@ -5865,19 +5206,9 @@ msgstr "Прочитать и принять" msgid "Terms and Conditions" msgstr "Условия использования" -msgid "" -"Thank you for purchasing a Bambu Lab device.Before using your Bambu Lab " -"device, please read the termsand conditions.By clicking to agree to use your " -"Bambu Lab device, you agree to abide by the Privacy Policyand Terms of " -"Use(collectively, the \"Terms\"). If you do not comply with or agree to the " -"Bambu Lab Privacy Policy, please do not use Bambu Lab equipment and services." +msgid "Thank you for purchasing a Bambu Lab device.Before using your Bambu Lab device, please read the termsand conditions.By clicking to agree to use your Bambu Lab device, you agree to abide by the Privacy Policyand Terms of Use(collectively, the \"Terms\"). If you do not comply with or agree to the Bambu Lab Privacy Policy, please do not use Bambu Lab equipment and services." msgstr "" -"Перед использованием устройства Bambu Lab ознакомьтесь с правилами и " -"условиями. Нажимая на кнопку \"Согласие на использование устройства Bambu Lab" -"\", вы соглашаетесь соблюдать Политику конфиденциальности и Условия " -"использования (далее - \"Условия\"). Если вы не соблюдаете или не согласны с " -"Политикой конфиденциальности Bambu Lab, пожалуйста, не пользуйтесь " -"оборудованием и услугами Bambu Lab." +"Перед использованием устройства Bambu Lab ознакомьтесь с правилами и условиями. Нажимая на кнопку \"Согласие на использование устройства Bambu Lab\", вы соглашаетесь соблюдать Политику конфиденциальности и Условия использования (далее - \"Условия\"). Если вы не соблюдаете или не согласны с Политикой конфиденциальности Bambu Lab, пожалуйста, не пользуйтесь оборудованием и услугами Bambu Lab." msgid "and" msgstr "и" @@ -5893,30 +5224,12 @@ msgstr "Заявление о программе улучшения пользо #, c-format, boost-format msgid "" -"In the 3D Printing community, we learn from each other's successes and " -"failures to adjust our own slicing parameters and settings. %s follows the " -"same principle and uses machine learning to improve its performance from the " -"successes and failures of the vast number of prints by our users. We are " -"training %s to be smarter by feeding them the real-world data. If you are " -"willing, this service will access information from your error logs and usage " -"logs, which may include information described in Privacy Policy. We will " -"not collect any Personal Data by which an individual can be identified " -"directly or indirectly, including without limitation names, addresses, " -"payment information, or phone numbers. By enabling this service, you agree " -"to these terms and the statement about Privacy Policy." +"In the 3D Printing community, we learn from each other's successes and failures to adjust our own slicing parameters and settings. %s follows the same principle and uses machine learning to improve its performance from the successes and failures of the vast number of prints by our users. We are training %s to be smarter by feeding them the real-world data. If you are willing, this service will " +"access information from your error logs and usage logs, which may include information described in Privacy Policy. We will not collect any Personal Data by which an individual can be identified directly or indirectly, including without limitation names, addresses, payment information, or phone numbers. By enabling this service, you agree to these terms and the statement about Privacy Policy." msgstr "" -"В сообществе 3D-печатников мы учимся на успехах и неудачах друг друга, чтобы " -"корректировать свои собственные параметры и настройки нарезки. Система %s " -"работает по тому же принципу и использует машинное обучение для улучшения " -"своей работы на основе успехов и неудач огромного количества отпечатков " -"наших пользователей. Мы обучаем %s быть умнее, предоставляя ему данные из " -"реального мира. По вашему желанию эта служба получит доступ к информации из " -"журналу ошибок и журналу использования, которая может включать информацию, " -"описанную в Политике конфиденциальности. Мы не будем собирать никаких " -"персональных данных, по которым можно прямо или косвенно идентифицировать " -"физическое лицо, включая, помимо прочего, имена, адреса, платежную " -"информацию или номера телефонов. Включая данную услугу, вы соглашаетесь с " -"данными условиями и заявлением о Политике конфиденциальности." +"В сообществе 3D-печатников мы учимся на успехах и неудачах друг друга, чтобы корректировать свои собственные параметры и настройки нарезки. Система %s работает по тому же принципу и использует машинное обучение для улучшения своей работы на основе успехов и неудач огромного количества отпечатков наших пользователей. Мы обучаем %s быть умнее, предоставляя ему данные из реального мира. По вашему " +"желанию эта служба получит доступ к информации из журналу ошибок и журналу использования, которая может включать информацию, описанную в Политике конфиденциальности. Мы не будем собирать никаких персональных данных, по которым можно прямо или косвенно идентифицировать физическое лицо, включая, помимо прочего, имена, адреса, платежную информацию или номера телефонов. Включая данную услугу, вы " +"соглашаетесь с данными условиями и заявлением о Политике конфиденциальности." msgid "Statement on User Experience Improvement Plan" msgstr "Заявление о плане улучшения взаимодействия с пользователем" @@ -5934,8 +5247,7 @@ msgid "Please log in first." msgstr "Пожалуйста, сначала авторизуйтесь." msgid "There was a problem connecting to the printer. Please try again." -msgstr "" -"Возникла проблема с подключением к принтеру. Пожалуйста, попробуйте ещё раз." +msgstr "Возникла проблема с подключением к принтеру. Пожалуйста, попробуйте ещё раз." msgid "Failed to log out." msgstr "Не удалось выйти." @@ -5952,33 +5264,18 @@ msgid "Search in preset" msgstr "Поиск в профиле" msgid "Click to reset all settings to the last saved preset." -msgstr "" -"Нажмите, чтобы сбросить все настройки до последнего сохраненного профиля." +msgstr "Нажмите, чтобы сбросить все настройки до последнего сохраненного профиля." + +msgid "Prime tower is required for smooth timeplase. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?" +msgstr "Для плавного таймлапса требуется черновая башня. На модели без использования черновой башни могут быть дефекты. Вы уверены, что хотите отключить черновую башню?" + +msgid "Prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Do you want to enable prime tower?" +msgstr "Для плавного таймлапса требуется черновая башня. На модели без использования черновой башни могут быть дефекты. Вы хотите включить черновую башню?" msgid "" -"Prime tower is required for smooth timeplase. There may be flaws on the " -"model without prime tower. Are you sure you want to disable prime tower?" -msgstr "" -"Для плавного таймлапса требуется черновая башня. На модели без использования " -"черновой башни могут быть дефекты. Вы уверены, что хотите отключить черновую " -"башню?" - -msgid "" -"Prime tower is required for smooth timelapse. There may be flaws on the " -"model without prime tower. Do you want to enable prime tower?" -msgstr "" -"Для плавного таймлапса требуется черновая башня. На модели без использования " -"черновой башни могут быть дефекты. Вы хотите включить черновую башню?" - -msgid "" -"We have added an experimental style \"Tree Slim\" that features smaller " -"support volume but weaker strength.\n" +"We have added an experimental style \"Tree Slim\" that features smaller support volume but weaker strength.\n" "We recommend using it with: 0 interface layers, 0 top distance, 2 walls." -msgstr "" -"Мы добавили экспериментальный стиль «Стройный (древ. поддержка)», который " -"отличается меньшим объёмом поддержки, а следовательно, и меньшей прочностью. " -"Мы рекомендуем использовать его со следующими параметрами: количество " -"связующих слоёв - 0, зазор поддержки сверху - 0, периметров - 2." +msgstr "Мы добавили экспериментальный стиль «Стройный (древ. поддержка)», который отличается меньшим объёмом поддержки, а следовательно, и меньшей прочностью. Мы рекомендуем использовать его со следующими параметрами: количество связующих слоёв - 0, зазор поддержки сверху - 0, периметров - 2." msgid "" "Change these settings automatically? \n" @@ -5989,10 +5286,7 @@ msgstr "" "Да - Изменить эти настройки автоматически\n" "Нет - Не изменять эти настройки" -msgid "" -"For \"Tree Strong\" and \"Tree Hybrid\" styles, we recommend the following " -"settings: at least 2 interface layers, at least 0.1mm top z distance or " -"using support materials on interface." +msgid "For \"Tree Strong\" and \"Tree Hybrid\" styles, we recommend the following settings: at least 2 interface layers, at least 0.1mm top z distance or using support materials on interface." msgstr "" "Для стилей «Крепкий (древ. поддержка)» и «Гибридный (древ. поддержка)» \n" "мы рекомендуем следующие параметры: \n" @@ -6001,10 +5295,8 @@ msgstr "" "или использование «материалов для поддержек» в качестве связующего слоя." msgid "" -"When using support material for the support interface, We recommend the " -"following settings:\n" -"0 top z distance, 0 interface spacing, concentric pattern and disable " -"independent support layer height" +"When using support material for the support interface, We recommend the following settings:\n" +"0 top z distance, 0 interface spacing, concentric pattern and disable independent support layer height" msgstr "" "При использовании «материалов для поддержек» в качестве связующего \n" "слоя поддержки, мы рекомендуем следующие параметры:\n" @@ -6014,15 +5306,11 @@ msgstr "" "отключение независимой высоты слоя поддержки." 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\"." +"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 "" -"При записи таймлапса без видимости головы рекомендуется добавить «Черновая " -"башня таймлапса». \n" -"Щелкните правой кнопкой мыши на пустом месте стола и выберите «Добавить " -"примитив» -> «Черновая башня таймлапса»." +"При записи таймлапса без видимости головы рекомендуется добавить «Черновая башня таймлапса». \n" +"Щелкните правой кнопкой мыши на пустом месте стола и выберите «Добавить примитив» -> «Черновая башня таймлапса»." msgid "Line width" msgstr "Ширина экструзии" @@ -6051,16 +5339,8 @@ msgstr "Скорость печати других слоёв" msgid "Overhang speed" msgstr "Скорость печати нависаний" -msgid "" -"This is the speed for various overhang degrees. Overhang degrees are " -"expressed as a percentage of line width. 0 speed means no slowing down for " -"the overhang degree range and wall speed is used" -msgstr "" -"Скорость печати нависаний разной степени свеса. Размер этого свеса " -"выражается в процентах от ширины линии. Скорость 0 означает, что для данного " -"диапазона нависаний замедление отсутствует и используется скорость " -"периметра. Скорость для промежуточных значений рассчитывается с помощью " -"линейной интерполяции." +msgid "This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used" +msgstr "Скорость печати нависаний разной степени свеса. Размер этого свеса выражается в процентах от ширины линии. Скорость 0 означает, что для данного диапазона нависаний замедление отсутствует и используется скорость периметра. Скорость для промежуточных значений рассчитывается с помощью линейной интерполяции." msgid "Bridge" msgstr "Мосты" @@ -6083,9 +5363,6 @@ msgstr "Подложка" msgid "Support filament" msgstr "Пруток для поддержки" -msgid "Tree supports" -msgstr "Древовидная поддержка" - msgid "Prime tower" msgstr "Черновая башня" @@ -6107,24 +5384,19 @@ msgstr "Частые" #, c-format, boost-format msgid "" "Following line %s contains reserved keywords.\n" -"Please remove it, or will beat G-code visualization and printing time " -"estimation." +"Please remove it, or will beat G-code visualization and printing time estimation." msgid_plural "" "Following lines %s contain reserved keywords.\n" -"Please remove them, or will beat G-code visualization and printing time " -"estimation." +"Please remove them, or will beat G-code visualization and printing time estimation." msgstr[0] "" "Следующая строка %s содержит зарезервированные ключевые слова.\n" -"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка " -"времени печати." +"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка времени печати." msgstr[1] "" "Следующие строки %s содержат зарезервированные ключевые слова.\n" -"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка " -"времени печати." +"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка времени печати." msgstr[2] "" "Следующие строки %s содержат зарезервированные ключевые слова.\n" -"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка " -"времени печати." +"Пожалуйста, удалите их, иначе будет нарушена визуализация G-кода и оценка времени печати." msgid "Reserved keywords found" msgstr "Найдены зарезервированные ключевые слова" @@ -6142,12 +5414,10 @@ msgid "Recommended nozzle temperature" msgstr "Рекомендуемая температура сопла" msgid "Recommended nozzle temperature range of this filament. 0 means no set" -msgstr "" -"Рекомендуемый диапазон температуры сопла для данной пластиковой нити. 0 " -"значит не задано." +msgstr "Рекомендуемый диапазон температуры сопла для данной пластиковой нити. 0 значит не задано." -msgid "Print chamber temperature" -msgstr "Температура в термокамере" +msgid "Recommended temperature range" +msgstr "Рекомендуемый диапазон температур" msgid "Print temperature" msgstr "Температура печати" @@ -6161,46 +5431,26 @@ msgstr "Температура сопла при печати" msgid "Cool plate" msgstr "Не нагреваемая пластина" -msgid "" -"Bed temperature when cool plate is installed. Value 0 means the filament " -"does not support to print on the Cool Plate" -msgstr "" -"Температура не подогреваемого стола. 0 означает, что пластиковая нить не " -"поддерживает печать на этой печатной пластине." +msgid "Bed temperature when cool plate is installed. Value 0 means the filament does not support to print on the Cool Plate" +msgstr "Температура не подогреваемого стола. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "Engineering plate" msgstr "Инженерная пластина" -msgid "" -"Bed temperature when engineering plate is installed. Value 0 means the " -"filament does not support to print on the Engineering Plate" -msgstr "" -"Температура стола при установленной инженерной печатной пластине. 0 " -"означает, что пластиковая нить не поддерживает печать на этой печатной " -"пластине." +msgid "Bed temperature when engineering plate is installed. Value 0 means the filament does not support to print on the Engineering Plate" +msgstr "Температура стола при установленной инженерной печатной пластине. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "Smooth PEI Plate / High Temp Plate" -msgstr "Гладкая PEI/высокотемпер. пластина" +msgid "High Temp Plate" +msgstr "Высокотемпературная пластина" -msgid "" -"Bed temperature when Smooth PEI Plate/High temperature plate is installed. " -"Value 0 means the filament does not support to print on the Smooth PEI Plate/" -"High Temp Plate" -msgstr "" -"Температура стола при установленной гладкой PEI/высокотемпературный печатной " -"пластине. 0 означает, что пластиковая нить не поддерживает печать на этой " -"печатной пластине." +msgid "Bed temperature when high temperature plate is installed. Value 0 means the filament does not support to print on the High Temp Plate" +msgstr "Температура стола при установленной высокотемпературной печатной пластине. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "Textured PEI Plate" msgstr "Текстурированная PEI пластина" -msgid "" -"Bed temperature when Textured PEI Plate is installed. Value 0 means the " -"filament does not support to print on the Textured PEI Plate" -msgstr "" -"Температура стола при установленной текстурированной пластите с PEI " -"покрытием. 0 означает, что пластиковая нить не поддерживает печать на этой " -"печатной пластине." +msgid "Bed temperature when Textured PEI Plate is installed. Value 0 means the filament does not support to print on the Textured PEI Plate" +msgstr "Температура стола при установленной текстурированной пластите с PEI покрытием. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "Volumetric speed limitation" msgstr "Ограничение объёмной скорости" @@ -6217,63 +5467,24 @@ msgstr "Вентилятор обдува модели" msgid "Min fan speed threshold" msgstr "Порог мин. скорости вентилятора" -msgid "" -"Part cooling fan speed will start to run at min speed when the estimated " -"layer time is no longer than the layer time in setting. When layer time is " -"shorter than threshold, fan speed is interpolated between the minimum and " -"maximum fan speed according to layer printing time" -msgstr "" -"Вентилятор для охлаждения моделей начнет работать с минимальной скоростью, " -"когда расчётное время печати слоя не превышает заданное время печати слоя. " -"Если время печати слоя меньше порогового значения, скорость вентилятора " -"интерполируется между минимальной и максимальной скоростью вентилятора в " -"зависимости от времени печати слоя." +msgid "Part cooling fan speed will start to run at min speed when the estimated layer time is no longer than the layer time in setting. When layer time is shorter than threshold, fan speed is interpolated between the minimum and maximum fan speed according to layer printing time" +msgstr "Вентилятор для охлаждения моделей начнет работать с минимальной скоростью, когда расчётное время печати слоя не превышает заданное время печати слоя. Если время печати слоя меньше порогового значения, скорость вентилятора интерполируется между минимальной и максимальной скоростью вентилятора в зависимости от времени печати слоя." msgid "Max fan speed threshold" msgstr "Порог макс. скорости вентилятора" -msgid "" -"Part cooling fan speed will be max when the estimated layer time is shorter " -"than the setting value" -msgstr "" -"Скорость вентилятора для охлаждения детали будет максимальной, если " -"расчётное время печати слоя меньше установленного значения." +msgid "Part cooling fan speed will be max when the estimated layer time is shorter than the setting value" +msgstr "Скорость вентилятора для охлаждения детали будет максимальной, если расчётное время печати слоя меньше установленного значения." msgid "Auxiliary part cooling fan" msgstr "Вспомогательный вентилятор для охлаждения моделей" -msgid "Exhaust fan" -msgstr "Вытяжной вентилятор" - -msgid "During print" -msgstr "Во время печати" - -msgid "Complete print" -msgstr "После завершения печати" - msgid "Filament start G-code" msgstr "Стартовый G-код прутка" msgid "Filament end G-code" msgstr "Завершающий G-код прутка" -msgid "Multimaterial" -msgstr "Экструдер ММ" - -msgid "Wipe tower parameters" -msgstr "Параметры черновой башни" - -msgid "Toolchange parameters with single extruder MM printers" -msgstr "" -"Параметры смены инструмента в одноэкструдерных мультиматериальных принтерах" - -msgid "Ramming settings" -msgstr "Настройки рэмминга" - -msgid "Toolchange parameters with multi extruder MM printers" -msgstr "" -"Параметры смены инструмента в мультиэкструдерных мультиматериальных принтерах" - msgid "Printable space" msgstr "Область печати" @@ -6304,14 +5515,11 @@ msgstr "G-код выполняемый перед сменой слоя" msgid "Layer change G-code" msgstr "G-код выполняемый при смене слоя" -msgid "Time lapse G-code" -msgstr "G-код таймлапса" - msgid "Change filament G-code" msgstr "G-код выполняемый при смене прутка" msgid "Change extrusion role G-code" -msgstr "" +msgstr "G-код выполняемый при смене роли экструзии" msgid "Pause G-code" msgstr "G-код паузы печати" @@ -6334,15 +5542,6 @@ msgstr "Ограничение ускорений" msgid "Jerk limitation" msgstr "Ограничение рывка" -msgid "Single extruder multimaterial setup" -msgstr "Мультиматериальный одиночный экструдер" - -msgid "Wipe tower" -msgstr "Черновая башня" - -msgid "Single extruder multimaterial parameters" -msgstr "Параметры мультиматериального одиночного экструдера" - msgid "Layer height limits" msgstr "Ограничение высоты слоя" @@ -6375,12 +5574,12 @@ msgstr[2] "Следующие профили также будут удален #, boost-format msgid "Are you sure to %1% the selected preset?" -msgstr "Вы уверены, что хотите %1% выбранный профиль?" +msgstr "%1% выбранный профиль?" #. TRN Remove/Delete #, boost-format msgid "%1% Preset" -msgstr "Профиль %1%" +msgstr "%1% профиль" msgid "All" msgstr "Все" @@ -6460,34 +5659,24 @@ msgid "Preset \"%1%\" contains the following unsaved changes:" msgstr "Профиль \"%1%\" имеет следующие несохранённые изменения:" #, boost-format -msgid "" -"Preset \"%1%\" is not compatible with the new printer profile and it " -"contains the following unsaved changes:" -msgstr "" -"Профиль \"%1%\" несовместим с новым профилем принтера, и имеет следующие " -"несохранённые изменения:" +msgid "Preset \"%1%\" is not compatible with the new printer profile and it contains the following unsaved changes:" +msgstr "Профиль \"%1%\" несовместим с новым профилем принтера, и имеет следующие несохранённые изменения:" #, boost-format -msgid "" -"Preset \"%1%\" is not compatible with the new process profile and it " -"contains the following unsaved changes:" -msgstr "" -"Профиль \"%1%\" несовместим с новым профилем процесса, и имеет следующие " -"несохранённые изменения:" +msgid "Preset \"%1%\" is not compatible with the new process profile and it contains the following unsaved changes:" +msgstr "Профиль \"%1%\" несовместим с новым профилем процесса, и имеет следующие несохранённые изменения:" #, boost-format msgid "" "You have changed some settings of preset \"%1%\". \n" -"Would you like to keep these changed settings (new value) after switching " -"preset?" +"Would you like to keep these changed settings (new value) after switching preset?" msgstr "" "Вы изменили некоторые параметры профиля \"%1%\". \n" "Хотите сохранить эти изменения (новые значения)?" msgid "" "You have changed some preset settings. \n" -"Would you like to keep these changed settings (new value) after switching " -"preset?" +"Would you like to keep these changed settings (new value) after switching preset?" msgstr "" "Вы изменили некоторые параметры профиля.\n" "Хотите сохранить эти изменения (новые значения)?" @@ -6587,44 +5776,6 @@ msgstr "Текущая конфигурация не требует обновл msgid "Ramming customization" msgstr "Настройки рэмминга" -msgid "" -"Ramming denotes the rapid extrusion just before a tool change in a single-" -"extruder MM printer. Its purpose is to properly shape the end of the " -"unloaded filament so it does not prevent insertion of the new filament and " -"can itself be reinserted later. This phase is important and different " -"materials can require different extrusion speeds to get the good shape. For " -"this reason, the extrusion rates during ramming are adjustable.\n" -"\n" -"This is an expert-level setting, incorrect adjustment will likely lead to " -"jams, extruder wheel grinding into filament etc." -msgstr "" -"Рэмминг (ramming, дословно утрамбовка) означает быстрое экструдирование " -"непосредственно перед сменой инструмента в одноэкструдерном " -"мультиматериальном принтере. Цель процесса состоит в том, чтобы правильно " -"сформировать конец выгружаемого прутка, чтобы он не препятствовал вставке " -"нового прутка или этого же прутка, вставленного позже. Эта фаза важна и " -"разные материалы могут потребовать разных скоростей экструзии, чтобы " -"получить хорошую форму. По этой причине скорость экструзии во время рэмминга " -"регулируется.\n" -"\n" -"Эта опция для опытных пользователей, неправильная настройка может привести к " -"замятию, протиранию прутка приводом экструдера и т.д." - -msgid "Total ramming time" -msgstr "Общее время рэмминга" - -msgid "s" -msgstr "с" - -msgid "Total rammed volume" -msgstr "Общий объём при рэмминге" - -msgid "Ramming line width" -msgstr "Ширина линии при рэмминге" - -msgid "Ramming line spacing" -msgstr "Расстояние между линиями при рэмминге" - msgid "Auto-Calc" msgstr "Авторасчёт" @@ -6666,8 +5817,7 @@ msgid "Login" msgstr "Войти" msgid "The configuration package is changed in previous Config Guide" -msgstr "" -"Пакет конфигурации был изменён при предыдущем запуске мастера настройки." +msgstr "Пакет конфигурации был изменён при предыдущем запуске мастера настройки." msgid "Configuration package changed" msgstr "Пакет конфигурации изменён" @@ -6699,7 +5849,7 @@ msgstr "" "устройств 3Dconnexion" msgid "Show keyboard shortcuts list" -msgstr "Показать список клавиш доступа к командам" +msgstr "Показать список сочетаний клавиш" msgid "Global shortcuts" msgstr "Глобальные горячие клавиши" @@ -6722,14 +5872,8 @@ msgstr "Shift+A" msgid "Shift+R" msgstr "Shift+R" -msgid "" -"Auto orientates selected objects or all objects.If there are selected " -"objects, it just orientates the selected ones.Otherwise, it will orientates " -"all objects in the current disk." -msgstr "" -"Автоориентация выбранных или всех моделей. При выбранных моделях, " -"ориентируются только они, противном случае ориентируются все модели на " -"текущем столе." +msgid "Auto orientates selected objects or all objects.If there are selected objects, it just orientates the selected ones.Otherwise, it will orientates all objects in the current disk." +msgstr "Автоориентация выбранных или всех моделей. При выбранных моделях, ориентируются только они, противном случае ориентируются все модели на текущем столе." msgid "Shift+Tab" msgstr "Shift+Tab" @@ -6854,6 +5998,9 @@ msgstr "Гизмо рисования шва (FDM)" msgid "Swtich between Prepare/Prewview" msgstr "Переключение между окном подготовки и окном предпросмотра нарезки" +msgid "Switch between Prepare/Prewview" +msgstr "Переключение между окном подготовки и окном предпросмотра нарезки" + msgid "Plater" msgstr "Печатная пластина" @@ -6915,8 +6062,7 @@ msgid "Horizontal slider - Move active thumb Right" msgstr "Горизонтальный ползунок - Сдвинуть активный ползунок вправо" msgid "On/Off one layer mode of the vertical slider" -msgstr "" -"Включение/Отключение функции «Режим одного слоя» у вертикального ползунка" +msgstr "Включение/Отключение функции «Режим одного слоя» у вертикального ползунка" msgid "On/Off g-code window" msgstr "Показать/скрыть окно отображения G-кода" @@ -6937,39 +6083,27 @@ msgstr "Информация об обновлении версии %s:" msgid "Network plug-in update" msgstr "Обновление сетевого плагина" -msgid "" -"Click OK to update the Network plug-in when Orca Slicer launches next time." -msgstr "" -"Нажмите OK, чтобы обновить сетевой плагин при следующем запуске Orca Slicer." +msgid "Click OK to update the Network plug-in when Bambu Studio launches next time." +msgstr "Нажмите OK, чтобы обновить сетевой плагин при следующем запуске Bambu Studio." #, c-format, boost-format msgid "A new Network plug-in(%s) available, Do you want to install it?" msgstr "Доступен новый сетевой плагин (%s). Хотите установить?" -msgid "New version of Orca Slicer" -msgstr "Доступна новая версия Orca Slicer" +msgid "New version of Bambu Studio" +msgstr "Доступна новая версия Bambu Studio" msgid "Don't remind me of this version again" msgstr "Больше не напоминай об этой версии" -msgid "Done" -msgstr "Готово" - msgid "LAN Connection Failed (Sending print file)" msgstr "Сбой подключения к локальной сети (отправка файла на печать)" -msgid "" -"Step 1, please confirm Orca Slicer and your printer are in the same LAN." -msgstr "" -"Шаг 1. Пожалуйста, убедитесь, что Orca Slicer и ваш принтер находятся в " -"одной локальной сети." +msgid "Step 1, please confirm Bambu Studio and your printer are in the same LAN." +msgstr "Шаг 1. Пожалуйста, убедитесь, что Bambu Studio и ваш принтер находятся в одной локальной сети." -msgid "" -"Step 2, if the IP and Access Code below are different from the actual values " -"on your printer, please correct them." -msgstr "" -"Шаг 2. Если приведенный ниже IP-адрес и код доступа отличаются от " -"фактических значений на вашем принтере, пожалуйста, исправьте их." +msgid "Step 2, if the IP and Access Code below are different from the actual values on your printer, please correct them." +msgstr "Шаг 2. Если приведенный ниже IP-адрес и код доступа отличаются от фактических значений на вашем принтере, пожалуйста, исправьте их." msgid "IP" msgstr "IP" @@ -7013,30 +6147,14 @@ msgstr "Сбой при обновлении" msgid "Updating successful" msgstr "Обновление успешно выполнено" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" -"Вы уверены, что хотите обновить? Это займёт около 10 минут. Не выключайте " -"питание во время обновления принтера." +msgid "Are you sure you want to update? This will take about 10 minutes. Do not turn off the power while the printer is updating." +msgstr "Вы уверены, что хотите обновить? Это займёт около 10 минут. Не выключайте питание во время обновления принтера." -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" -"Было обнаружено важное обновление, которое необходимо установить перед " -"продолжением печати. Хотите обновиться сейчас? Обновление можно выполнить и " -"позже, нажав «Обновить прошивку»." +msgid "An important update was detected and needs to be run before printing can continue. Do you want to update now? You can also update later from 'Upgrade firmware'." +msgstr "Было обнаружено важное обновление, которое необходимо установить перед продолжением печати. Хотите обновиться сейчас? Обновление можно выполнить и позже, нажав «Обновить прошивку»." -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" -"Ошибка в версии прошивки. Перед печатью её необходимо исправить и обновить. " -"Хотите обновить сейчас? Вы можете сделать это позже с принтера или при " -"следующем запуске программы." +msgid "The firmware version is abnormal. Repairing and updating are required before printing. Do you want to update now? You can also update later on printer or update next time starting the studio." +msgstr "Ошибка в версии прошивки. Перед печатью её необходимо исправить и обновить. Хотите обновить сейчас? Вы можете сделать это позже с принтера или при следующем запуске программы." msgid "Extension Board" msgstr "Плата расширения" @@ -7094,8 +6212,7 @@ msgid "Copying of file %1% to %2% failed: %3%" msgstr "Не удалось скопировать файл %1% в %2%: %3%" msgid "Need to check the unsaved changes before configuration updates." -msgstr "" -"Перед обновлением конфигурации необходимо проверить несохранённые изменения." +msgstr "Перед обновлением конфигурации необходимо проверить несохранённые изменения." msgid "Configuration package updated to " msgstr "Пакет конфигурации обновлён до " @@ -7103,12 +6220,8 @@ msgstr "Пакет конфигурации обновлён до " msgid "Open G-code file:" msgstr "Выберите G-код файл:" -msgid "" -"One object has empty initial layer and can't be printed. Please Cut the " -"bottom or enable supports." -msgstr "" -"Одна модель имеет пустой начальный слой и не может быть напечатана. " -"Пожалуйста, обрежьте нижнюю часть или включите поддержки." +msgid "One object has empty initial layer and can't be printed. Please Cut the bottom or enable supports." +msgstr "Одна модель имеет пустой начальный слой и не может быть напечатана. Пожалуйста, обрежьте нижнюю часть или включите поддержки." #, boost-format msgid "Object can't be printed for empty layer between %1% and %2%." @@ -7118,12 +6231,8 @@ msgstr "Модель не может быть напечатан из-за пу msgid "Object: %1%" msgstr "Модель: %1%" -msgid "" -"Maybe parts of the object at these height are too thin, or the object has " -"faulty mesh" -msgstr "" -"Возможно, части модели на этой высоте слишком тонкие, или она имеет " -"дефектную сетку." +msgid "Maybe parts of the object at these height are too thin, or the object has faulty mesh" +msgstr "Возможно, части модели на этой высоте слишком тонкие, или она имеет дефектную сетку." msgid "No object can be printed. Maybe too small" msgstr "Печать моделей невозможна. Возможно, они слишком маленькие." @@ -7131,14 +6240,10 @@ msgstr "Печать моделей невозможна. Возможно, он msgid "" "Failed to generate gcode for invalid custom G-code.\n" "\n" -msgstr "" -"Не удалось сгенерировать G-код из-за недопустимого пользовательского G-" -"кода.\n" +msgstr "Не удалось сгенерировать G-код из-за недопустимого пользовательского G-кода.\n" msgid "Please check the custom G-code or use the default custom G-code." -msgstr "" -"Пожалуйста, проверьте пользовательский G-код или используйте " -"пользовательский G-код по умолчанию." +msgstr "Пожалуйста, проверьте пользовательский G-код или используйте пользовательский G-код по умолчанию." #, boost-format msgid "Generating G-code: layer %1%" @@ -7185,15 +6290,7 @@ msgstr "Множитель" #, boost-format msgid "Failed to calculate line width of %1%. Can not get value of \"%2%\" " -msgstr "" -"Не удалось вычислить ширину линии %1%. Не удается получить значение \"%2%\". " - -msgid "" -"Invalid spacing supplied to Flow::with_spacing(), check your layer height " -"and extrusion width" -msgstr "" -"Для Flow::with_spacing () был указан недопустимый интервал. Проверьте высоту " -"слоя и ширину экструзии." +msgstr "Не удалось вычислить ширину линии %1%. Не удается получить значение \"%2%\". " msgid "undefined error" msgstr "неопределённая ошибка" @@ -7289,139 +6386,67 @@ msgid "write callback failed" msgstr "ошибка записи функции обратного вызова" #, boost-format -msgid "" -"%1% is too close to exclusion area, there may be collisions when printing." -msgstr "" -"%1% находится слишком близко к области исключения, что может привести к " -"столкновению при печати." +msgid "%1% is too close to exclusion area, there may be collisions when printing." +msgstr "%1% находится слишком близко к области исключения, что может привести к столкновению при печати." #, boost-format msgid "%1% is too close to others, and collisions may be caused." -msgstr "" -"%1% находится слишком близко к другим, что может привести к столкновению." +msgstr "%1% находится слишком близко к другим, что может привести к столкновению." #, boost-format msgid "%1% is too tall, and collisions will be caused." msgstr "%1% слишком высокий, что может привести к столкновению." msgid " is too close to others, there may be collisions when printing." -msgstr "" -" находится слишком близко к другим моделям, что может привести к " -"столкновению при печати." +msgstr " находится слишком близко к другим моделям, что может привести к столкновению при печати." msgid " is too close to exclusion area, there may be collisions when printing." -msgstr "" -" находится слишком близко к области исключения, что может привести к " -"столкновению при печати." +msgstr " находится слишком близко к области исключения, что может привести к столкновению при печати." msgid "Prime Tower" msgstr "Черновая башня" msgid " is too close to others, and collisions may be caused.\n" -msgstr "" -" находится слишком близко к другим моделям, что может привести к " -"столкновению.\n" +msgstr " находится слишком близко к другим моделям, что может привести к столкновению.\n" msgid " is too close to exclusion area, and collisions will be caused.\n" -msgstr "" -" находится слишком близко к области исключения, что может привести к " -"столкновению.\n" +msgstr " находится слишком близко к области исключения, что может привести к столкновению.\n" -msgid "" -"Can not print multiple filaments which have large difference of temperature " -"together. Otherwise, the extruder and nozzle may be blocked or damaged " -"during printing" -msgstr "" -"Не допускается совместная печать несколькими материалами, имеющими большую " -"разницу в температуре печати. Это может привести к засорению и повреждению " -"сопла и экструдера." +msgid "Can not print multiple filaments which have large difference of temperature together. Otherwise, the extruder and nozzle may be blocked or damaged during printing" +msgstr "Не допускается совместная печать несколькими материалами, имеющими большую разницу в температуре печати. Это может привести к засорению и повреждению сопла и экструдера." msgid "No extrusions under current settings." msgstr "При текущих настройках экструзия отсутствует." -msgid "" -"Smooth mode of timelapse is not supported when \"by object\" sequence is " -"enabled." -msgstr "" -"Плавный режим таймлапса не поддерживается, когда включена последовательность " -"печати моделей по очереди." +msgid "Smooth mode of timelapse is not supported when \"by object\" sequence is enabled." +msgstr "Плавный режим таймлапса не поддерживается, когда включена последовательность печати моделей по очереди." -msgid "" -"Please select \"By object\" print sequence to print multiple objects in " -"spiral vase mode." -msgstr "" -"Выберите последовательность печати «По очереди», для поддержки печати " -"несколько моделей в режиме спиральной вазы." +msgid "Please select \"By object\" print sequence to print multiple objects in spiral vase mode." +msgstr "Выберите последовательность печати «По очереди», для поддержки печати несколько моделей в режиме спиральной вазы." -msgid "" -"The spiral vase mode does not work when an object contains more than one " -"materials." -msgstr "" -"Режим «Спиральная ваза» не работает, когда модель печатается несколькими " -"материалами." - -#, boost-format -msgid "The object %1% exceeds the maximum build volume height." -msgstr "Высота модели %1% превышает максимально допустимую." - -#, boost-format -msgid "" -"While the object %1% itself fits the build volume, its last layer exceeds " -"the maximum build volume height." -msgstr "" -"Хотя сама модель %1% вписывается в область построения, её последний слой " -"превышает максимальную высоту области построения." - -msgid "" -"You might want to reduce the size of your model or change current print " -"settings and retry." -msgstr "" -"Попробуйте уменьшить размер модели или изменить текущие настройки печати и " -"повторить попытку." - -msgid "Variable layer height is not supported with Organic supports." -msgstr "" -"Функция переменной высоты слоя не совместима органическими поддержками." +msgid "The spiral vase mode does not work when an object contains more than one materials." +msgstr "Режим «Спиральная ваза» не работает, когда модель печатается несколькими материалами." msgid "The prime tower is not supported in \"By object\" print." msgstr "Черновая башня не поддерживается при печати в режиме «По очереди»." -msgid "" -"The prime tower is not supported when adaptive layer height is on. It " -"requires that all objects have the same layer height." -msgstr "" -"Черновой башни не поддерживается, когда включена функция переменной высоты " -"слоя. Требуется, чтобы все модели имели одинаковую высоту слоя." +msgid "The prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height." +msgstr "Черновой башни не поддерживается, когда включена функция переменной высоты слоя. Требуется, чтобы все модели имели одинаковую высоту слоя." msgid "The prime tower requires \"support gap\" to be multiple of layer height" -msgstr "" -"Для черновой башни требуется, чтобы зазор поддержки был кратен высоте слоя." +msgstr "Для черновой башни требуется, чтобы зазор поддержки был кратен высоте слоя." msgid "The prime tower requires that all objects have the same layer heights" -msgstr "" -"Для использования черновой башни требуется, чтобы у всех моделей была " -"одинаковая высота слоя." +msgstr "Для использования черновой башни требуется, чтобы у всех моделей была одинаковая высота слоя." -msgid "" -"The prime tower requires that all objects are printed over the same number " -"of raft layers" -msgstr "" -"Для черновой башни требуется, чтобы все модели были напечатаны на одинаковом " -"количестве слоёв подложки." +msgid "The prime tower requires that all objects are printed over the same number of raft layers" +msgstr "Для черновой башни требуется, чтобы все модели были напечатаны на одинаковом количестве слоёв подложки." -msgid "" -"The prime tower requires that all objects are sliced with the same layer " -"heights." -msgstr "" -"Для использования черновой башни требуется, чтобы все модели были нарезаны с " -"одинаковой высотой слоя." +msgid "The prime tower requires that all objects are sliced with the same layer heights." +msgstr "Для использования черновой башни требуется, чтобы все модели были нарезаны с одинаковой высотой слоя." -msgid "" -"The prime tower is only supported if all objects have the same variable " -"layer height" -msgstr "" -"Для черновой башни требуется, чтобы все модели имели одинаковую переменную " -"высоту слоя." +msgid "The prime tower is only supported if all objects have the same variable layer height" +msgstr "Для черновой башни требуется, чтобы все модели имели одинаковую переменную высоту слоя." msgid "Too small line width" msgstr "Слишком маленькая ширина экструзии" @@ -7429,67 +6454,38 @@ msgstr "Слишком маленькая ширина экструзии" msgid "Too large line width" msgstr "Слишком большая ширина экструзии" -msgid "" -"The prime tower requires that support has the same layer height with object." -msgstr "" -"Для черновой башни требуется, чтобы поддержка и модель имели одинаковую " -"высоту слоя." +msgid "The prime tower requires that support has the same layer height with object." +msgstr "Для черновой башни требуется, чтобы поддержка и модель имели одинаковую высоту слоя." + +msgid "Tree supports" +msgstr "Древовидная поддержка" # ??? -msgid "" -"Organic support tree tip diameter must not be smaller than support material " -"extrusion width." -msgstr "" -"Диаметр кончика ветки органической поддержки не должен быть меньше значения " -"ширины экструзии поддержки." +msgid "Organic support tree tip diameter must not be smaller than support material extrusion width." +msgstr "Диаметр кончика ветки органической поддержки не должен быть меньше значения ширины экструзии поддержки." # ??? -msgid "" -"Organic support branch diameter must not be smaller than 2x support material " -"extrusion width." -msgstr "" -"Диаметр ветки органической поддержки должен быть хотя бы в два раза больше " -"значения ширины экструзии поддержки." +msgid "Organic support branch diameter must not be smaller than 2x support material extrusion width." +msgstr "Диаметр ветки органической поддержки должен быть хотя бы в два раза больше значения ширины экструзии поддержки." # ??? -msgid "" -"Organic support branch diameter must not be smaller than support tree tip " -"diameter." -msgstr "" -"Диаметр ветвей органической поддержки должен быть больше диаметра кончика " -"ветки." +msgid "Organic support branch diameter must not be smaller than support tree tip diameter." +msgstr "Диаметр ветвей органической поддержки должен быть больше диаметра кончика ветки." -msgid "" -"Support enforcers are used but support is not enabled. Please enable support." -msgstr "" -"Используется принудительная поддержка, но её генерация не включена. " -"Пожалуйста, включите генерацию поддержки в настройках слайсера." +msgid "Support enforcers are used but support is not enabled. Please enable support." +msgstr "Используется принудительная поддержка, но её генерация не включена. Пожалуйста, включите генерацию поддержки в настройках слайсера." msgid "Layer height cannot exceed nozzle diameter" msgstr "Высота слоя не может быть больше диаметра сопла" -msgid "" -"Relative extruder addressing requires resetting the extruder position at " -"each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to " -"layer_gcode." -msgstr "" -"При относительной адресации экструдера его положение необходимо " -"корректировать на каждом слое, чтобы предотвратить потерю точности с " -"плавающей запятой. Добавьте \"G92 E0\" в layer_gcode." +msgid "Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode." +msgstr "При относительной адресации экструдера его положение необходимо корректировать на каждом слое, чтобы предотвратить потерю точности с плавающей запятой. Добавьте \"G92 E0\" в layer_gcode." -msgid "" -"\"G92 E0\" was found in before_layer_gcode, which is incompatible with " -"absolute extruder addressing." -msgstr "" -"В before_layer_gcode была найдена команда \"G92 E0\", которая несовместима с " -"абсолютной адресацией экструдера." +msgid "\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing." +msgstr "В before_layer_gcode была найдена команда \"G92 E0\", которая несовместима с абсолютной адресацией экструдера." -msgid "" -"\"G92 E0\" was found in layer_gcode, which is incompatible with absolute " -"extruder addressing." -msgstr "" -"В layer_gcode была найдена команда \"G92 E0\", которая несовместима с " -"абсолютной адресацией экструдера." +msgid "\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing." +msgstr "В layer_gcode была найдена команда \"G92 E0\", которая несовместима с абсолютной адресацией экструдера." #, c-format, boost-format msgid "Plate %d: %s does not support filament %s" @@ -7513,15 +6509,8 @@ msgstr "Область печати" msgid "Bed exclude area" msgstr "Область исключения" -msgid "" -"Unprintable area in XY plane. For example, X1 Series printers use the front " -"left corner to cut filament during filament change. The area is expressed as " -"polygon by points in following format: \"XxY, XxY, ...\"" -msgstr "" -"Непечатаемая область в плоскости XY. Например, в принтерах серии X1 передний " -"левый угол используется для обрезания материала при его замене. Область " -"выражается в виде многоугольника по точкам в следующем формате: \"XxY, " -"XxY, ...\"" +msgid "Unprintable area in XY plane. For example, X1 Series printers use the front left corner to cut filament during filament change. The area is expressed as polygon by points in following format: \"XxY, XxY, ...\"" +msgstr "Непечатаемая область в плоскости XY. Например, в принтерах серии X1 передний левый угол используется для обрезания материала при его замене. Область выражается в виде многоугольника по точкам в следующем формате: \"XxY, XxY, ...\"" msgid "Bed custom texture" msgstr "Пользовательская текстура стола" @@ -7532,35 +6521,11 @@ msgstr "Пользовательская модель стола" msgid "Elephant foot compensation" msgstr "Компенсация «слоновьей ноги»" -msgid "" -"Shrink the initial layer on build plate to compensate for elephant foot " -"effect" -msgstr "" -"Уменьшение первого слоя в плоскости XY на заданное значение, чтобы " -"компенсировать эффект слоновьей ноги." +msgid "Shrink the initial layer on build plate to compensate for elephant foot effect" +msgstr "Уменьшение первого слоя в плоскости XY на заданное значение, чтобы компенсировать эффект слоновьей ноги." -msgid "Elephant foot compensation layers" -msgstr "Компенсирующих слоёв «слоновьей ноги»" - -msgid "" -"The number of layers on which the elephant foot compensation will be active. " -"The first layer will be shrunk by the elephant foot compensation value, then " -"the next layers will be linearly shrunk less, up to the layer indicated by " -"this value." -msgstr "" -"Количество слоёв, на которые будет распространяться компенсация слоновьей " -"ноги. Первый слой будет уменьшен на величину компенсации слоновьей ноги с " -"последующим линейным уменьшением до слоя, указанного здесь." - -msgid "layers" -msgstr "слой(-я)" - -msgid "" -"Slicing height for each layer. Smaller layer height means more accurate and " -"more printing time" -msgstr "" -"Высота каждого слоя. Чем меньше значение, тем лучше качество, но требуется " -"больше времени для печати, и наоборот." +msgid "Slicing height for each layer. Smaller layer height means more accurate and more printing time" +msgstr "Высота каждого слоя. Чем меньше значение, тем лучше качество, но требуется больше времени для печати, и наоборот." msgid "Printable height" msgstr "Высота печати" @@ -7574,37 +6539,20 @@ msgstr "Имена профиля принтера" msgid "Hostname, IP or URL" msgstr "Имя хоста, IP/URL-адрес" -msgid "" -"Slic3r can upload G-code files to a printer host. This field should contain " -"the hostname, IP address or URL of the printer host instance. Print host " -"behind HAProxy with basic auth enabled can be accessed by putting the user " -"name and password into the URL in the following format: https://username:" -"password@your-octopi-address/" -msgstr "" -"Slic3r может загружать файл G-кода на хост принтера. В этом поле нужно " -"указать имя хоста, IP-адрес или URL-адрес хост-экземпляра печати. Доступ к " -"узлу печати на основе HAProxy с включенной базовой аутентификацией можно " -"получить, указав имя пользователя и пароль в поле URL-адрес в следующем " -"формате: https://username:password@your-octopi-address" +msgid "Slic3r can upload G-code files to a printer host. This field should contain the hostname, IP address or URL of the printer host instance. Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL in the following format: https://username:password@your-octopi-address/" +msgstr "Slic3r может загружать файл G-кода на хост принтера. В этом поле нужно указать имя хоста, IP-адрес или URL-адрес хост-экземпляра печати. Доступ к узлу печати на основе HAProxy с включенной базовой аутентификацией можно получить, указав имя пользователя и пароль в поле URL-адрес в следующем формате: https://username:password@your-octopi-address" msgid "Device UI" msgstr "URL-адрес хоста" -msgid "" -"Specify the URL of your device user interface if it's not same as print_host" -msgstr "" -"Укажите URL-адрес пользовательского интерфейса вашего устройства, если он не " -"совпадает с print_host" +msgid "Specify the URL of your device user interface if it's not same as print_host" +msgstr "Укажите URL-адрес пользовательского интерфейса вашего устройства, если он не совпадает с print_host" msgid "API Key / Password" msgstr "API-ключ/Пароль" -msgid "" -"Slic3r can upload G-code files to a printer host. This field should contain " -"the API Key or the password required for authentication." -msgstr "" -"Slic3r может загружать файл G-кода на хост принтера. Это поле должно " -"содержать API ключ или пароль, необходимые для проверки подлинности." +msgid "Slic3r can upload G-code files to a printer host. This field should contain the API Key or the password required for authentication." +msgstr "Slic3r может загружать файл G-кода на хост принтера. Это поле должно содержать API ключ или пароль, необходимые для проверки подлинности." msgid "Name of the printer" msgstr "Название принтера" @@ -7612,14 +6560,8 @@ msgstr "Название принтера" msgid "HTTPS CA File" msgstr "Файл корневого сертификата HTTPS" -msgid "" -"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " -"in crt/pem format. If left blank, the default OS CA certificate repository " -"is used." -msgstr "" -"Для подключений по HTTPS к OctoPrint укажите пользовательский файл корневого " -"сертификата в формате crt/pem. Если оставить поле пустым, будет " -"использоваться хранилище сертификатов ОС по умолчанию." +msgid "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. If left blank, the default OS CA certificate repository is used." +msgstr "Для подключений по HTTPS к OctoPrint укажите пользовательский файл корневого сертификата в формате crt/pem. Если оставить поле пустым, будет использоваться хранилище сертификатов ОС по умолчанию." msgid "User" msgstr "Пользователь" @@ -7630,14 +6572,8 @@ msgstr "Пароль" msgid "Ignore HTTPS certificate revocation checks" msgstr "Игнорировать проверки отзыва HTTPS сертификата" -msgid "" -"Ignore HTTPS certificate revocation checks in case of missing or offline " -"distribution points. One may want to enable this option for self signed " -"certificates if connection fails." -msgstr "" -"Игнорировать проверки отзыва HTTPS сертификата в случае его отсутствия или " -"автономности точек распространения. Можно включить эту опцию для " -"самоподписанных сертификатов в случае сбоя подключения." +msgid "Ignore HTTPS certificate revocation checks in case of missing or offline distribution points. One may want to enable this option for self signed certificates if connection fails." +msgstr "Игнорировать проверки отзыва HTTPS сертификата в случае его отсутствия или автономности точек распространения. Можно включить эту опцию для самоподписанных сертификатов в случае сбоя подключения." msgid "Names of presets related to the physical printer" msgstr "Имена профилей, связанных с физическим принтером" @@ -7655,24 +6591,13 @@ msgid "Avoid crossing wall" msgstr "Избегать пересечения периметров" msgid "Detour and avoid to travel across wall which may cause blob on surface" -msgstr "" -"Объезжать и избегать пересечения периметров, для предотвращения образования " -"дефектов на поверхности модели." +msgstr "Объезжать и избегать пересечения периметров, для предотвращения образования дефектов на поверхности модели." msgid "Avoid crossing wall - Max detour length" msgstr "Избегать пересечения периметров - Макс. длина обхода" -msgid "" -"Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value. Detour length could be specified " -"either as an absolute value or as percentage (for example 50%) of a direct " -"travel path. Zero to disable" -msgstr "" -"Максимальное расстояние обхода сопла от модели во избежание пересечения " -"периметров при движении. Если расстояние обхода превышает это значение, то " -"для данного маршрута эта опция не применяется. Длина обхода может быть " -"задана как в абсолютном значении, так и в процентах (например, 50%) от " -"прямого пути перемещения. 0 - отключено." +msgid "Maximum detour distance for avoiding crossing wall. Don't detour if the detour distance is large than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. Zero to disable" +msgstr "Максимальное расстояние обхода сопла от модели во избежание пересечения периметров при движении. Если расстояние обхода превышает это значение, то для данного маршрута эта опция не применяется. Длина обхода может быть задана как в абсолютном значении, так и в процентах (например, 50%) от прямого пути перемещения. 0 - отключено." msgid "mm or %" msgstr "мм или %" @@ -7680,36 +6605,20 @@ msgstr "мм или %" msgid "Other layers" msgstr "Последующие слои" -msgid "" -"Bed temperature for layers except the initial one. Value 0 means the " -"filament does not support to print on the Cool Plate" -msgstr "" -"Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая " -"нить не поддерживает печать на этой печатной пластине." +msgid "Bed temperature for layers except the initial one. Value 0 means the filament does not support to print on the Cool Plate" +msgstr "Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "°C" msgstr "°C" -msgid "" -"Bed temperature for layers except the initial one. Value 0 means the " -"filament does not support to print on the Engineering Plate" -msgstr "" -"Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая " -"нить не поддерживает печать на этой печатной пластине." +msgid "Bed temperature for layers except the initial one. Value 0 means the filament does not support to print on the Engineering Plate" +msgstr "Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "" -"Bed temperature for layers except the initial one. Value 0 means the " -"filament does not support to print on the High Temp Plate" -msgstr "" -"Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая " -"нить не поддерживает печать на этой печатной пластине." +msgid "Bed temperature for layers except the initial one. Value 0 means the filament does not support to print on the High Temp Plate" +msgstr "Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "" -"Bed temperature for layers except the initial one. Value 0 means the " -"filament does not support to print on the Textured PEI Plate" -msgstr "" -"Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая " -"нить не поддерживает печать на этой печатной пластине." +msgid "Bed temperature for layers except the initial one. Value 0 means the filament does not support to print on the Textured PEI Plate" +msgstr "Температура стола для всех слоёв, кроме первого. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "Initial layer" msgstr "Первый слой" @@ -7717,33 +6626,17 @@ msgstr "Первый слой" msgid "Initial layer bed temperature" msgstr "Температура стола для первого слоя" -msgid "" -"Bed temperature of the initial layer. Value 0 means the filament does not " -"support to print on the Cool Plate" -msgstr "" -"Температура стола для первого слоя. 0 означает, что пластиковая нить не " -"поддерживает печать на этой печатной пластине." +msgid "Bed temperature of the initial layer. Value 0 means the filament does not support to print on the Cool Plate" +msgstr "Температура стола для первого слоя. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "" -"Bed temperature of the initial layer. Value 0 means the filament does not " -"support to print on the Engineering Plate" -msgstr "" -"Температура стола для первого слоя. 0 означает, что пластиковая нить не " -"поддерживает печать на этой печатной пластине." +msgid "Bed temperature of the initial layer. Value 0 means the filament does not support to print on the Engineering Plate" +msgstr "Температура стола для первого слоя. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "" -"Bed temperature of the initial layer. Value 0 means the filament does not " -"support to print on the High Temp Plate" -msgstr "" -"Температура стола для первого слоя. 0 означает, что пластиковая нить не " -"поддерживает печать на этой печатной пластине." +msgid "Bed temperature of the initial layer. Value 0 means the filament does not support to print on the High Temp Plate" +msgstr "Температура стола для первого слоя. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." -msgid "" -"Bed temperature of the initial layer. Value 0 means the filament does not " -"support to print on the Textured PEI Plate" -msgstr "" -"Температура стола для первого слоя. 0 означает, что пластиковая нить не " -"поддерживает печать на этой печатной пластине." +msgid "Bed temperature of the initial layer. Value 0 means the filament does not support to print on the Textured PEI Plate" +msgstr "Температура стола для первого слоя. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." msgid "Bed types supported by the printer" msgstr "Типы столов, поддерживаемые принтером" @@ -7758,226 +6651,104 @@ msgid "First layer print sequence" msgstr "Последовательность печати первого слоя" msgid "This G-code is inserted at every layer change before lifting z" -msgstr "" -"Этот G-код вставляется при каждой смене слоя, непосредственно перед " -"перемещения оси Z." +msgstr "Этот G-код вставляется при каждой смене слоя, непосредственно перед перемещения оси Z." msgid "Bottom shell layers" msgstr "Сплошных слоёв снизу" -msgid "" -"This is the number of solid layers of bottom shell, including the bottom " -"surface layer. When the thickness calculated by this value is thinner than " -"bottom shell thickness, the bottom shell layers will be increased" -msgstr "" -"Количество сплошных слоёв при печати нижней поверхности модели, включая " -"нижний поверхностный слой. Если толщина, рассчитанная с помощью этого " -"значения, меньше толщины оболочки снизу, количество слоёв оболочки снизу " -"будет увеличено." +msgid "This is the number of solid layers of bottom shell, including the bottom surface layer. When the thickness calculated by this value is thinner than bottom shell thickness, the bottom shell layers will be increased" +msgstr "Количество сплошных слоёв при печати нижней поверхности модели, включая нижний поверхностный слой. Если толщина, рассчитанная с помощью этого значения, меньше толщины оболочки снизу, количество слоёв оболочки снизу будет увеличено." msgid "Bottom shell thickness" msgstr "Толщина оболочки снизу" -msgid "" -"The number of bottom solid layers is increased when slicing if the thickness " -"calculated by bottom shell layers is thinner than this value. This can avoid " -"having too thin shell when layer height is small. 0 means that this setting " -"is disabled and thickness of bottom shell is absolutely determained by " -"bottom shell layers" +msgid "The number of bottom solid layers is increased when slicing if the thickness calculated by bottom shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of bottom shell is absolutely determained by bottom shell layers" msgstr "" -"Минимальная толщина оболочки снизу в мм. Если толщина оболочки, рассчитанная " -"по количеству сплошных слоёв снизу, меньше этого значения, количество " -"сплошных слоёв снизу будет автоматически увеличено при нарезке, для " -"удовлетворения минимальной толщины оболочки. Это позволяет избежать слишком " -"тонкой оболочки при небольшой высоте слоя. 0 означает, что этот параметр " -"отключён, а толщина оболочки снизу полностью задаётся количеством сплошных " -"слоёв снизу." +"Минимальная толщина оболочки снизу в мм. Если толщина оболочки, рассчитанная по количеству сплошных слоёв снизу, меньше этого значения, количество сплошных слоёв снизу будет автоматически увеличено при нарезке, для удовлетворения минимальной толщины оболочки. Это позволяет избежать слишком тонкой оболочки при небольшой высоте слоя. 0 означает, что этот параметр отключён, а толщина оболочки " +"снизу полностью задаётся количеством сплошных слоёв снизу." msgid "Force cooling for overhang and bridge" msgstr "Принудительный обдув навесов и мостов" -msgid "" -"Enable this option to optimize part cooling fan speed for overhang and " -"bridge to get better cooling" -msgstr "" -"Включите, чтобы оптимизировать скорость вентилятора охлаждения моделей для " -"нависаний и мостов для обеспечения лучшего их охлаждения." +msgid "Enable this option to optimize part cooling fan speed for overhang and bridge to get better cooling" +msgstr "Включите, чтобы оптимизировать скорость вентилятора охлаждения моделей для нависаний и мостов для обеспечения лучшего их охлаждения." msgid "Fan speed for overhang" msgstr "Скорость вентилятора на нависанияx" -msgid "" -"Force part cooling fan to be this speed when printing bridge or overhang " -"wall which has large overhang degree. Forcing cooling for overhang and " -"bridge can get better quality for these part" -msgstr "" -"Заставляет вентилятор обдува модели работать на этой скорости при печати " -"мостов или нависающих периметров, имеющих большую степень свеса. " -"Принудительное охлаждение позволяет повысить качество печати этих частей." +msgid "Force part cooling fan to be this speed when printing bridge or overhang wall which has large overhang degree. Forcing cooling for overhang and bridge can get better quality for these part" +msgstr "Заставляет вентилятор обдува модели работать на этой скорости при печати мостов или нависающих периметров, имеющих большую степень свеса. Принудительное охлаждение позволяет повысить качество печати этих частей." msgid "Cooling overhang threshold" msgstr "Порог включения обдува на нависаниях" -#, fuzzy, c-format -msgid "" -"Force cooling fan to be specific speed when overhang degree of printed part " -"exceeds this value. Expressed as percentage which indicides how much width " -"of the line without support from lower layer. 0% means forcing cooling for " -"all outer wall no matter how much overhang degree" -msgstr "" -"Принудительное включение вентилятора обдува модели на определенную скорость, " -"если степень нависания печатаемой части превышает данное значение. " -"Выражается в процентах и показывает, насколько велика ширина периметра без " -"поддержки со стороны нижнего слоя. 0% означает принудительное охлаждение " -"всего внешнего периметра независимо от величина свеса." +msgid "Force cooling fan to be specific speed when overhang degree of printed part exceeds this value. Expressed as percentage which indicides how much width of the line without support from lower layer. 0% means forcing cooling for all outer wall no matter how much overhang degree" +msgstr "Принудительное включение вентилятора обдува модели на определенную скорость, если степень нависания печатаемой части превышает данное значение. Выражается в процентах и показывает, насколько велика ширина периметра без поддержки со стороны нижнего слоя. 0% означает принудительное охлаждение всего внешнего периметра независимо от величина свеса." msgid "Bridge infill direction" msgstr "Угол печати мостов" -msgid "" -"Bridging angle override. If left to zero, the bridging angle will be " -"calculated automatically. Otherwise the provided angle will be used for " -"external bridges. Use 180°for zero angle." -msgstr "" -"Переопределение угла печати мостов. Если задано 0, угол печати мостов " -"рассчитывается автоматически. В противном случае заданный угол будет " -"использоваться для наружных мостов. Для нулевого угла установите 180°." +msgid "Bridging angle override. If left to zero, the bridging angle will be calculated automatically. Otherwise the provided angle will be used for external bridges. Use 180°for zero angle." +msgstr "Переопределение угла печати мостов. Если задано 0, угол печати мостов рассчитывается автоматически. В противном случае заданный угол будет использоваться для наружных мостов. Для нулевого угла установите 180°." msgid "Bridge density" msgstr "Плотность мостов" msgid "Density of external bridges. 100% means solid bridge. Default is 100%." -msgstr "" -"Плотность наружных мостов. 100% - сплошной мост. По умолчанию задано 100%." +msgstr "Плотность наружных мостов. 100% - сплошной мост. По умолчанию задано 100%." msgid "Bridge flow" msgstr "Поток при печати мостов" -msgid "" -"Decrease this value slightly(for example 0.9) to reduce the amount of " -"material for bridge, to improve sag" -msgstr "" -"Параметр задаёт количество пластика, затрачиваемое для построения мостов. В " -"большинстве случаев настроек по умолчанию достаточно, тем не менее, при " -"печати некоторых моделей уменьшение параметра может сократить провисание " -"пластика при печати мостов." +msgid "Decrease this value slightly(for example 0.9) to reduce the amount of material for bridge, to improve sag" +msgstr "Параметр задаёт количество пластика, затрачиваемое для построения мостов. В большинстве случаев настроек по умолчанию достаточно, тем не менее, при печати некоторых моделей уменьшение параметра может сократить провисание пластика при печати мостов." msgid "Top surface flow ratio" msgstr "Коэффициент потока на верхней поверхности" -msgid "" -"This factor affects the amount of material for top solid infill. You can " -"decrease it slightly to have smooth surface finish" -msgstr "" -"Этот параметр задаёт количество выдавливаемого материала для верхнего " -"сплошного слоя заполнения. Вы можете немного уменьшить его, чтобы получить " -"более гладкую поверхность." +msgid "This factor affects the amount of material for top solid infill. You can decrease it slightly to have smooth surface finish" +msgstr "Этот параметр задаёт количество выдавливаемого материала для верхнего сплошного слоя заполнения. Вы можете немного уменьшить его, чтобы получить более гладкую поверхность." msgid "Bottom surface flow ratio" msgstr "Коэффициент потока на нижней поверхности" msgid "This factor affects the amount of material for bottom solid infill" -msgstr "" -"Этот параметр задаёт количество выдавливаемого материала для нижнего " -"сплошного слоя заполнения." +msgstr "Этот параметр задаёт количество выдавливаемого материала для нижнего сплошного слоя заполнения." msgid "Precise wall(experimental)" msgstr "Точные периметры (экспериментально)" -msgid "" -"Improve shell precision by adjusting outer wall spacing. This also improves " -"layer consistency." -msgstr "" -"Повышение точности оболочки за счет регулировки расстояния между внешними " -"стенками. Это также позволяет уменьшить расслоение слоёв." +msgid "Improve shell precision by adjusting outer wall spacing. This also improves layer consistency." +msgstr "Повышение точности оболочки за счет регулировки расстояния между внешними стенками. Это также позволяет уменьшить расслоение слоёв." msgid "Only one wall on top surfaces" msgstr "Только один периметр на верхней поверхности" -msgid "" -"Use only one wall on flat top surface, to give more space to the top infill " -"pattern" -msgstr "" -"Печатать только один периметр на верхней поверхности, чтобы оставить больше " -"пространства для верхнего шаблона заполнения." +msgid "Use only one wall on flat top surface, to give more space to the top infill pattern" +msgstr "Печатать только один периметр на верхней поверхности, чтобы оставить больше пространства для верхнего шаблона заполнения." msgid "One wall threshold" msgstr "Порог одного периметра" -#, fuzzy, c-format, boost-format msgid "" -"If a top surface has to be printed and it's partially covered by another " -"layer, it won't be considered at a top layer where its width is below this " -"value. This can be useful to not let the 'one perimeter on top' trigger on " -"surface that should be covered only by perimeters. This value can be a mm or " -"a % of the perimeter extrusion width.\n" -"Warning: If enabled, artifacts can be created is you have some thin features " -"on the next layer, like letters. Set this setting to 0 to remove these " -"artifacts." +"If a top surface has to be printed and it's partially covered by another layer, it won't be considered at a top layer where its width is below this value. This can be useful to not let the 'one perimeter on top' trigger on surface that should be covered only by perimeters. This value can be a mm or a % of the perimeter extrusion width.\n" +"Warning: If enabled, artifacts can be created is you have some thin features on the next layer, like letters. Set this setting to 0 to remove these artifacts." msgstr "" -"Если должна быть напечатана верхняя поверхность и частично покрыта другим " -"слоем, она не будет рассматриваться как верхний слой, ширина которого ниже " -"этого значения. Это может быть полезно, чтобы не допустить срабатывания " -"функции «Только один периметр на верхней поверхности» на поверхности, " -"которая должна быть покрыта только периметрами. Это значение может быть " -"задано в мм или % от ширины экструзии периметра.\n" -"Предупреждение: если этот параметр включён, то могут возникнуть артефакты, " -"если у вас на следующем слое имеются какие-то тонкие элементы, например, " -"буквы. Установите значение 0, чтобы убрать эти артефакты." +"Если должна быть напечатана верхняя поверхность и частично покрыта другим слоем, она не будет рассматриваться как верхний слой, ширина которого ниже этого значения. Это может быть полезно, чтобы не допустить срабатывания функции «Только один периметр на верхней поверхности» на поверхности, которая должна быть покрыта только периметрами. Это значение может быть задано в мм или % от ширины " +"экструзии периметра.\n" +"Предупреждение: если этот параметр включён, то могут возникнуть артефакты, если у вас на следующем слое имеются какие-то тонкие элементы, например, буквы. Установите значение 0, чтобы убрать эти артефакты." msgid "Only one wall on first layer" msgstr "Только один периметр на первом слое" -msgid "" -"Use only one wall on first layer, to give more space to the bottom infill " -"pattern" -msgstr "" -"Печатать только один периметр на первом слое, чтобы оставить больше " -"пространства для нижнего шаблона заполнения." +msgid "Use only one wall on first layer, to give more space to the bottom infill pattern" +msgstr "Печатать только один периметр на первом слое, чтобы оставить больше пространства для нижнего шаблона заполнения." msgid "Extra perimeters on overhangs" msgstr "Дополнительные периметры на нависаниях" -msgid "" -"Create additional perimeter paths over steep overhangs and areas where " -"bridges cannot be anchored. " -msgstr "" -"Создание дополнительных дорожек по периметру над крутыми нависаниями и " -"участками, где мосты не могут быть закреплены." - -# от Overhang reversal -# Реверс на нечётных слоях -msgid "Reverse on odd" -msgstr "Реверс на нависаниях" - -msgid "Overhang reversal" -msgstr "Реверс на нависаниях" - -msgid "" -"Extrude perimeters that have a part over an overhang in the reverse " -"direction on odd layers. This alternating pattern can drastically improve " -"steep overhang." -msgstr "" -"Печать периметров, имеющих нависания, в обратном направлении на нечётных " -"слоях. Такое чередование может значительно улучшить качество печати крутых " -"нависаний." - -# от Overhang reversal threshold -msgid "Reverse threshold" -msgstr "Порог для реверса" - -msgid "Overhang reversal threshold" -msgstr "Порог разворота на свесах" - -#, fuzzy, c-format, boost-format -msgid "" -"Number of mm the overhang need to be for the reversal to be considered " -"useful. Can be a % of the perimeter width.\n" -"Value 0 enables reversal on every odd layers regardless." -msgstr "" -"Величина свеса периметра при которой она считается достаточной для активации " -"функции реверса печати нависаний.\n" -"Может быть задано как в процентах, так и в миллиметрах от ширины периметра." +msgid "Create additional perimeter paths over steep overhangs and areas where bridges cannot be anchored. " +msgstr "Создание дополнительных дорожек по периметру над крутыми нависаниями и участками, где мосты не могут быть закреплены." msgid "Classic mode" msgstr "Классический режим" @@ -7991,17 +6762,6 @@ msgstr "Замедляться при печати нависаний" msgid "Enable this option to slow printing down for different overhang degree" msgstr "Включение динамического управления скоростью печати нависаний." -# ??? Замедление печати на изогнутых периметров -msgid "Slow down for curled perimeters" -msgstr "Снижение скорости на изогнутых периметрах" - -msgid "" -"Enable this option to slow printing down in areas where potential curled " -"perimeters may exist" -msgstr "" -"Включите эту опцию для замедления печати в тех областях, где потенциально " -"могут возникать изогнутые периметры." - msgid "mm/s or %" msgstr "мм/с или %" @@ -8017,12 +6777,8 @@ msgstr "мм/с" msgid "Internal" msgstr "Внутренние" -msgid "" -"Speed of internal bridge. If the value is expressed as a percentage, it will " -"be calculated based on the bridge_speed. Default value is 150%." -msgstr "" -"Скорость печати внутреннего моста. Если задано в процентах, то значение " -"вычисляться относительно bridge_speed. Значение по умолчанию равно 150%." +msgid "Speed of internal bridge. If the value is expressed as a percentage, it will be calculated based on the bridge_speed. Default value is 150%." +msgstr "Скорость печати внутреннего моста. Если задано в процентах, то значение вычисляться относительно bridge_speed. Значение по умолчанию равно 150%." msgid "Brim width" msgstr "Ширина каймы" @@ -8033,20 +6789,13 @@ msgstr "Расстояние от модели до внешней линии к msgid "Brim type" msgstr "Тип каймы" -msgid "" -"This controls the generation of the brim at outer and/or inner side of " -"models. Auto means the brim width is analysed and calculated automatically." -msgstr "" -"Этот параметр управляет формированием каймы на внешней/внутренней стороне " -"моделей. Авто означает, что ширина каймы анализируется и рассчитывается " -"автоматически." +msgid "This controls the generation of the brim at outer and/or inner side of models. Auto means the brim width is analysed and calculated automatically." +msgstr "Этот параметр управляет формированием каймы на внешней/внутренней стороне моделей. Авто означает, что ширина каймы анализируется и рассчитывается автоматически." msgid "Brim-object gap" msgstr "Смещение каймы" -msgid "" -"A gap between innermost brim line and object can make brim be removed more " -"easily" +msgid "A gap between innermost brim line and object can make brim be removed more easily" msgstr "Смещение каймы от печатаемой модели, может облегчить её удаление." msgid "Brim ears" @@ -8071,12 +6820,10 @@ msgid "Brim ear detection radius" msgstr "Радиус обнаружения ушек каймы" msgid "" -"The geometry will be decimated before dectecting sharp angles. This " -"parameter indicates the minimum length of the deviation for the decimation.\n" +"The geometry will be decimated before dectecting sharp angles. This parameter indicates the minimum length of the deviation for the decimation.\n" "0 to deactivate" msgstr "" -"Геометрия модели будет упрощена перед обнаружением острых углов. Этот " -"параметр задаёт минимальную длину отклонения для её упрощения.\n" +"Геометрия модели будет упрощена перед обнаружением острых углов. Этот параметр задаёт минимальную длину отклонения для её упрощения.\n" "Установите 0 для отключения." msgid "Compatible machine" @@ -8106,24 +6853,14 @@ msgstr "По очереди" msgid "Slow printing down for better layer cooling" msgstr "Замедлять печать для лучшего охлаждения слоёв" -msgid "" -"Enable this option to slow printing speed down to make the final layer time " -"not shorter than the layer time threshold in \"Max fan speed threshold\", so " -"that layer can be cooled for longer time. This can improve the cooling " -"quality for needle and small details" -msgstr "" -"Включите эту опцию для разрешения замедления скорости печати в зависимости " -"от времени печати слоя, чтобы слой мог охлаждаться дольше. Это позволяет " -"улучшить качество охлаждения острых концов и мелких деталей." +msgid "Enable this option to slow printing speed down to make the final layer time not shorter than the layer time threshold in \"Max fan speed threshold\", so that layer can be cooled for longer time. This can improve the cooling quality for needle and small details" +msgstr "Включите эту опцию для разрешения замедления скорости печати в зависимости от времени печати слоя, чтобы слой мог охлаждаться дольше. Это позволяет улучшить качество охлаждения острых концов и мелких деталей." msgid "Normal printing" msgstr "Ускорение по умолчанию" -msgid "" -"The default acceleration of both normal printing and travel except initial " -"layer" -msgstr "" -"Ускорение по умолчанию для обычной печати и перемещения, кроме первого слоя." +msgid "The default acceleration of both normal printing and travel except initial layer" +msgstr "Ускорение по умолчанию для обычной печати и перемещения, кроме первого слоя." msgid "mm/s²" msgstr "мм/с²" @@ -8132,81 +6869,40 @@ msgid "Default filament profile" msgstr "Профиль прутка по умолчанию" msgid "Default filament profile when switch to this machine profile" -msgstr "" -"Профиль пластиковой нити по умолчанию при переключении на этот профиль " -"принтера." +msgstr "Профиль пластиковой нити по умолчанию при переключении на этот профиль принтера." msgid "Default process profile" msgstr "Профиль процесса по умолчанию" msgid "Default process profile when switch to this machine profile" -msgstr "" -"Профиль процесса по умолчанию при переключении на этот профиль принтера." - -msgid "Activate air filtration" -msgstr "Вкл. фильтрацию воздуха" - -msgid "Activate for better air filtration. G-code command: M106 P3 S(0-255)" -msgstr "" -"Активировать для лучшей фильтрации воздуха. G-код команда: M106 P3 S(0-255)" - -msgid "Fan speed" -msgstr "Скорость вентилятора" - -msgid "" -"Speed of exhuast fan during printing.This speed will overwrite the speed in " -"filament custom gcode" -msgstr "" -"Скорость вытяжного вентилятора во время печати. Эта скорость будет " -"переопределять скорость в пользовательском стартовом G-коде материала" - -msgid "Speed of exhuast fan after printing completes" -msgstr "Скорость вытяжного вентилятора после завершения печати" +msgstr "Профиль процесса по умолчанию при переключении на этот профиль принтера." msgid "No cooling for the first" msgstr "Не включать вентилятор на первых" -msgid "" -"Close all cooling fan for the first certain layers. Cooling fan of the first " -"layer used to be closed to get better build plate adhesion" -msgstr "" -"Вы можете задать положительное значение, чтобы отключить все вентиляторы " -"охлаждения модели при печати первых нескольких слоёв, чтобы не ухудшить " -"адгезию к столу." +msgid "Close all cooling fan for the first certain layers. Cooling fan of the first layer used to be closed to get better build plate adhesion" +msgstr "Вы можете задать положительное значение, чтобы отключить все вентиляторы охлаждения модели при печати первых нескольких слоёв, чтобы не ухудшить адгезию к столу." + +msgid "layers" +msgstr "слой(-я)" msgid "Don't support bridges" msgstr "Не печатать поддержки под мостами" -msgid "" -"Don't support the whole bridge area which make support very large. Bridge " -"usually can be printing directly without support if not very long" -msgstr "" -"Опция, препятствующая печати поддержки под мостами. Мост обычно можно " -"печатать без поддержки, если он не очень длинный." +msgid "Don't support the whole bridge area which make support very large. Bridge usually can be printing directly without support if not very long" +msgstr "Опция, препятствующая печати поддержки под мостами. Мост обычно можно печатать без поддержки, если он не очень длинный." msgid "Thick bridges" msgstr "Толстые мосты" -msgid "" -"If enabled, bridges are more reliable, can bridge longer distances, but may " -"look worse. If disabled, bridges look better but are reliable just for " -"shorter bridged distances." -msgstr "" -"Если включено, мосты печатаются более надежные и на большие расстояния. Если " -"отключено, мосты выглядят лучше, но они надежны только на коротких " -"расстояниях." +msgid "If enabled, bridges are more reliable, can bridge longer distances, but may look worse. If disabled, bridges look better but are reliable just for shorter bridged distances." +msgstr "Если включено, мосты печатаются более надежные и на большие расстояния. Если отключено, мосты выглядят лучше, но они надежны только на коротких расстояниях." msgid "Max bridge length" msgstr "Максимальная длина моста" -msgid "" -"Max length of bridges that don't need support. Set it to 0 if you want all " -"bridges to be supported, and set it to a very large value if you don't want " -"any bridges to be supported." -msgstr "" -"Максимальная длина мостов, не нуждающихся в поддержке. Установите 0, если " -"требуется поддержка всех мостов, или очень большое значение, если поддержка " -"мостов не требуется." +msgid "Max length of bridges that don't need support. Set it to 0 if you want all bridges to be supported, and set it to a very large value if you don't want any bridges to be supported." +msgstr "Максимальная длина мостов, не нуждающихся в поддержке. Установите 0, если требуется поддержка всех мостов, или очень большое значение, если поддержка мостов не требуется." msgid "End G-code" msgstr "Завершающий G-код" @@ -8218,14 +6914,16 @@ msgid "End G-code when finish the printing of this filament" msgstr "Завершающий G-код при окончании печати этой пластиковой нитью." msgid "Ensure vertical shell thickness" -msgstr "" +msgstr "Обеспечивать верт. толщину оболочки" -msgid "" -"Add solid infill near sloping surfaces to guarantee the vertical shell " -"thickness (top+bottom solid layers)" -msgstr "" -"Добавление сплошного заполнения вблизи наклонных поверхностей для " -"обеспечения вертикальной толщины оболочки (верхний+нижний сплошные слои)." +msgid "Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)" +msgstr "Добавление сплошного заполнения вблизи наклонных поверхностей для обеспечения вертикальной толщины оболочки (верхний+нижний сплошные слои)." + +msgid "Internal bridge support thickness" +msgstr "Толщина поддержки внутреннего моста" + +msgid "If enabled, support loops will be generated under the contours of internal bridges.These support loops could prevent internal bridges from extruding over the air and improve the top surface quality, especially when the sparse infill density is low.This value determines the thickness of the support loops. 0 means disable this feature" +msgstr "Если включено, под контурами внутренних мостов будут создаваться петли поддержки. Эти петли поддержки могут препятствовать выдавливанию внутренних мостов в воздух и улучшить качество поверхности сверху, особенно при низкой плотности заполнения. Это значение определяет толщину петель поддержки. Установите 0 для отключения." msgid "Top surface pattern" msgstr "Шаблон заполнения верхней поверхности" @@ -8266,56 +6964,32 @@ msgstr "Шаблон заполнения нижней поверхности, msgid "Internal solid infill pattern" msgstr "Шаблон сплошного заполнения" -msgid "" -"Line pattern of internal solid infill. if the detect nattow internal solid " -"infill be enabled, the concentric pattern will be used for the small area." -msgstr "" -"Шаблон печати внутреннего сплошного заполнения. Если включена функция " -"«Обнаруживать узкую область сплошного заполнения», то для небольшой области " -"будет использоваться концентрический шаблон заполнения." +msgid "Line pattern of internal solid infill. if the detect nattow internal solid infill be enabled, the concentric pattern will be used for the small area." +msgstr "Шаблон печати внутреннего сплошного заполнения. Если включена функция «Обнаруживать узкую область сплошного заполнения», то для небольшой области будет использоваться концентрический шаблон заполнения." -msgid "" -"Line width of outer wall. If expressed as a %, it will be computed over the " -"nozzle diameter." -msgstr "" -"Ширина экструзии для внешнего периметра. Если задано в процентах, то " -"значение вычисляться относительно диаметра сопла." +msgid "Line width of outer wall. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для внешнего периметра. Если задано в процентах, то значение вычисляться относительно диаметра сопла." -msgid "" -"Speed of outer wall which is outermost and visible. It's used to be slower " -"than inner wall speed to get better quality." -msgstr "" -"Скорость печати внешнего периметра (видимого). Для улучшения качества, эту " -"скорость делают ниже скорости внутренних периметров." +msgid "Speed of outer wall which is outermost and visible. It's used to be slower than inner wall speed to get better quality." +msgstr "Скорость печати внешнего периметра (видимого). Для улучшения качества, эту скорость делают ниже скорости внутренних периметров." msgid "Small perimeters" msgstr "Маленькие периметры" -msgid "" -"This separate setting will affect the speed of perimeters having radius <= " -"small_perimeter_threshold (usually holes). If expressed as percentage (for " -"example: 80%) it will be calculated on the outer wall speed setting above. " -"Set to zero for auto." -msgstr "" -"Этот параметр влияет на скорость печати периметров, имеющих радиус <= " -"значению порога маленьких периметров (обычно это отверстия). Если задано в " -"процентах, параметр вычисляется относительно скорости печати внешнего " -"периметра указанного выше. Установите 0 для автонастройки." +msgid "This separate setting will affect the speed of perimeters having radius <= small_perimeter_threshold (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the outer wall speed setting above. Set to zero for auto." +msgstr "Этот параметр влияет на скорость печати периметров, имеющих радиус <= значению порога маленьких периметров (обычно это отверстия). Если задано в процентах, параметр вычисляется относительно скорости печати внешнего периметра указанного выше. Установите 0 для автонастройки." msgid "Small perimeters threshold" msgstr "Порог маленьких периметров" -msgid "" -"This sets the threshold for small perimeter length. Default threshold is 0mm" -msgstr "" -"Пороговое значение длины маленьких периметров. Значение по умолчанию - 0 мм." +msgid "This sets the threshold for small perimeter length. Default threshold is 0mm" +msgstr "Пороговое значение длины маленьких периметров. Значение по умолчанию - 0 мм." msgid "Order of inner wall/outer wall/infil" msgstr "Порядок печати периметров/заполнения" msgid "Print sequence of inner wall, outer wall and infill. " -msgstr "" -"Последовательность печати внутреннего/внешнего периметров и заполнения. " +msgstr "Последовательность печати внутреннего/внешнего периметров и заполнения. " msgid "inner/outer/infill" msgstr "внутренний/внешний/заполнение" @@ -8335,37 +7009,23 @@ msgstr "внутренний-внешний-внутренний/заполне msgid "Height to rod" msgstr "Высота до вала" -msgid "" -"Distance of the nozzle tip to the lower rod. Used for collision avoidance in " -"by-object printing." -msgstr "" -"Расстояние от кончика сопла до нижнего вала. Значение важно при печати " -"моделей «По очереди» для предотвращения столкновений." +msgid "Distance of the nozzle tip to the lower rod. Used for collision avoidance in by-object printing." +msgstr "Расстояние от кончика сопла до нижнего вала. Значение важно при печати моделей «По очереди» для предотвращения столкновений." msgid "Height to lid" msgstr "Высота до крышки" -msgid "" -"Distance of the nozzle tip to the lid. Used for collision avoidance in by-" -"object printing." -msgstr "" -"Расстояние от кончика сопла до крышки. Значение важно при печати моделей «По " -"очереди» для предотвращения столкновений." +msgid "Distance of the nozzle tip to the lid. Used for collision avoidance in by-object printing." +msgstr "Расстояние от кончика сопла до крышки. Значение важно при печати моделей «По очереди» для предотвращения столкновений." -msgid "" -"Clearance radius around extruder. Used for collision avoidance in by-object " -"printing." -msgstr "" -"Безопасное расстояние вокруг экструдера. Используется для предотвращения " -"столкновений при печати отдельно стоящих моделей." +msgid "Clearance radius around extruder. Used for collision avoidance in by-object printing." +msgstr "Безопасное расстояние вокруг экструдера. Используется для предотвращения столкновений при печати отдельно стоящих моделей." msgid "Extruder Color" msgstr "Цвет экструдера" msgid "Only used as a visual help on UI" -msgstr "" -"Используется только в качестве визуальной помощи в пользовательском " -"интерфейсе" +msgstr "Используется только в качестве визуальной помощи в пользовательском интерфейсе" msgid "Extruder offset" msgstr "Смещение экструдера по осям X/Y" @@ -8373,64 +7033,37 @@ msgstr "Смещение экструдера по осям X/Y" msgid "Flow ratio" msgstr "Коэффициент потока" -msgid "" -"The material may have volumetric change after switching between molten state " -"and crystalline state. This setting changes all extrusion flow of this " -"filament in gcode proportionally. Recommended value range is between 0.95 " -"and 1.05. Maybe you can tune this value to get nice flat surface when there " -"has slight overflow or underflow" +msgid "The material may have volumetric change after switching between molten state and crystalline state. This setting changes all extrusion flow of this filament in gcode proportionally. Recommended value range is between 0.95 and 1.05. Maybe you can tune this value to get nice flat surface when there has slight overflow or underflow" msgstr "" -"Коэффициент пропорционального изменения величины потока подаваемого " -"пластика. Рекомендуемый диапазон значений от 0,95 до 1,05.\n" -"При небольшом переливе или недоливе на поверхности, корректировка этого " -"параметра поможет получить хорошую гладкую поверхность." +"Коэффициент пропорционального изменения величины потока подаваемого пластика. Рекомендуемый диапазон значений от 0,95 до 1,05.\n" +"При небольшом переливе или недоливе на поверхности, корректировка этого параметра поможет получить хорошую гладкую поверхность." msgid "Enable pressure advance" msgstr "Включить Pressure advance" -msgid "" -"Enable pressure advance, auto calibration result will be overwriten once " -"enabled." -msgstr "" -"Включить Pressure advance (Прогнозирование давления). Результат " -"автокалибровки будет перезаписан после включения." +msgid "Enable pressure advance, auto calibration result will be overwriten once enabled." +msgstr "Включить Pressure advance (Прогнозирование давления). Результат автокалибровки будет перезаписан после включения." msgid "Pressure advance(Klipper) AKA Linear advance factor(Marlin)" -msgstr "" -"Pressure advance (Прогнозирование давления) в прошивки Klipper, это одно и " -"тоже что Linear advance в прошивке Marlin." +msgstr "Pressure advance (Прогнозирование давления) в прошивки Klipper, это одно и тоже что Linear advance в прошивке Marlin." -msgid "" -"Default line width if other line widths are set to 0. If expressed as a %, " -"it will be computed over the nozzle diameter." -msgstr "" -"Ширина экструзии по умолчанию, если какие-либо из значений ширины экструзии " -"установлены равные нулю. Если задано в процентах, то значение вычисляться " -"относительно диаметра сопла." +msgid "Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии по умолчанию, если какие-либо из значений ширины экструзии установлены равные нулю. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Keep fan always on" msgstr "Вентилятор включён всегда" -msgid "" -"If enable this setting, part cooling fan will never be stoped and will run " -"at least at minimum speed to reduce the frequency of starting and stoping" -msgstr "" -"Если включено, вентилятор охлаждения модели никогда не будет останавливаться " -"и будет работать на минимальной скорости, чтобы сократить частоту его " -"запуска и остановки." +msgid "If enable this setting, part cooling fan will never be stoped and will run at least at minimum speed to reduce the frequency of starting and stoping" +msgstr "Если включено, вентилятор охлаждения модели никогда не будет останавливаться и будет работать на минимальной скорости, чтобы сократить частоту его запуска и остановки." msgid "Layer time" msgstr "Время слоя" -msgid "" -"Part cooling fan will be enabled for layers of which estimated time is " -"shorter than this value. Fan speed is interpolated between the minimum and " -"maximum fan speeds according to layer printing time" -msgstr "" -"Вентилятор охлаждения моделей будет включён для слоёв, расчётное время " -"которых меньше этого значения. Скорость вентилятора интерполируется между " -"минимальной и максимальной скоростями вентилятора зависимости от времени " -"печати слоя." +msgid "Part cooling fan will be enabled for layers of which estimated time is shorter than this value. Fan speed is interpolated between the minimum and maximum fan speeds according to layer printing time" +msgstr "Вентилятор охлаждения моделей будет включён для слоёв, расчётное время которых меньше этого значения. Скорость вентилятора интерполируется между минимальной и максимальной скоростями вентилятора зависимости от времени печати слоя." + +msgid "s" +msgstr "с" msgid "Default color" msgstr "Цвет по умолчанию" @@ -8450,201 +7083,45 @@ msgstr "Здесь вы можете написать свои замечани msgid "Required nozzle HRC" msgstr "Необходимая твёрдость сопла" -msgid "" -"Minimum HRC of nozzle required to print the filament. Zero means no checking " -"of nozzle's HRC." -msgstr "" -"Минимальная твёрдость материала сопла (HRC), необходимая для печати " -"пластиковой нитью. 0 - отключение контроля сопел на твёрдость." +msgid "Minimum HRC of nozzle required to print the filament. Zero means no checking of nozzle's HRC." +msgstr "Минимальная твёрдость материала сопла (HRC), необходимая для печати пластиковой нитью. 0 - отключение контроля сопел на твёрдость." -msgid "" -"This setting stands for how much volume of filament can be melted and " -"extruded per second. Printing speed is limited by max volumetric speed, in " -"case of too high and unreasonable speed setting. Can't be zero" -msgstr "" -"Этот параметр определяет, какой объём материала может быть расплавлен и " -"выдавлен в секунду. Скорость печати ограничена максимальной объёмной " -"скоростью в случае слишком высокой и необоснованной установки скорости. " -"Параметр не может быть нулевым." +msgid "This setting stands for how much volume of filament can be melted and extruded per second. Printing speed is limited by max volumetric speed, in case of too high and unreasonable speed setting. Can't be zero" +msgstr "Этот параметр определяет, какой объём материала может быть расплавлен и выдавлен в секунду. Скорость печати ограничена максимальной объёмной скоростью в случае слишком высокой и необоснованной установки скорости. Параметр не может быть нулевым." msgid "mm³/s" msgstr "мм³/с" +msgid "Minimal purge on wipe tower" +msgstr "Мин. объём сброса на черновой башне" + +msgid "After a tool change, the exact position of the newly loaded filament inside the nozzle may not be known, and the filament pressure is likely not yet stable. Before purging the print head into an infill or a sacrificial object, Slic3r will always prime this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably." +msgstr "После смены инструмента, точное положение вновь загруженного прутка внутри него может быть неизвестно, и давление прутка, вероятно, ещё не стабильно. Перед тем, как очистить печатающую головку в заполнение или в «жертвенную» модель Slic3r всегда будет выдавливать это количество материала на черновую башню, чтобы обеспечить надёжную печать заполнения или «жертвенной» модели." + msgid "Filament load time" msgstr "Время загрузки прутка" msgid "Time to load new filament when switch filament. For statistics only" -msgstr "" -"Время загрузки новой пластиковой нити при её смене. Только для статистики." +msgstr "Время загрузки новой пластиковой нити при её смене. Только для статистики." msgid "Filament unload time" msgstr "Время выгрузки прутка" msgid "Time to unload old filament when switch filament. For statistics only" -msgstr "" -"Время выгрузки старой пластиковой нити при её смене. Только для статистики." +msgstr "Время выгрузки старой пластиковой нити при её смене. Только для статистики." -msgid "" -"Filament diameter is used to calculate extrusion in gcode, so it's important " -"and should be accurate" -msgstr "" -"Диаметр пластиковой нити используется для расчёта экструзии, поэтому он " -"важен и должен быть точным" +msgid "Filament diameter is used to calculate extrusion in gcode, so it's important and should be accurate" +msgstr "Диаметр пластиковой нити используется для расчёта экструзии, поэтому он важен и должен быть точным" msgid "Shrinkage" msgstr "Усадка материала" -#, fuzzy, c-format, boost-format msgid "" -"Enter the shrinkage percentage that the filament will get after cooling " -"(94% if you measure 94mm instead of 100mm). The part will be scaled in xy to " -"compensate. Only the filament used for the perimeter is taken into account.\n" -"Be sure to allow enough space between objects, as this compensation is done " -"after the checks." +"Enter the shrinkage percentage that the filament will get after cooling (94% if you measure 94mm instead of 100mm). The part will be scaled in xy to compensate. Only the filament used for the perimeter is taken into account.\n" +"Be sure to allow enough space between objects, as this compensation is done after the checks." msgstr "" -"Введите процент усадки пластиковой нити, которую получит она после " -"охлаждения (пишите 94%, если вы намерили 94 мм, вместо 100 мм). Для " -"компенсации усадки деталь будет отмасштабированна по оси XY. При этом " -"учитывается только пластиковая нить, используемая для печати внешнего " -"периметра.\n" -"Убедитесь, что между моделями достаточно места, так как эта компенсация " -"выполняется после проверок." - -msgid "Loading speed" -msgstr "Скорость загрузки" - -msgid "Speed used for loading the filament on the wipe tower." -msgstr "Скорость загрузки прутка при печати черновой башни." - -msgid "Loading speed at the start" -msgstr "Начальная скорость загрузки" - -msgid "Speed used at the very beginning of loading phase." -msgstr "Скорость в начальной фазе загрузки прутка." - -msgid "Unloading speed" -msgstr "Скорость выгрузки" - -msgid "" -"Speed used for unloading the filament on the wipe tower (does not affect " -"initial part of unloading just after ramming)." -msgstr "" -"Скорость выгрузки прутка на черновую башню. (не влияет на начальную фазу " -"выгрузки сразу после рэмминга)." - -msgid "Unloading speed at the start" -msgstr "Начальная скорость выгрузки" - -msgid "" -"Speed used for unloading the tip of the filament immediately after ramming." -msgstr "Скорость выгрузки кончика прутка сразу после рэмминга." - -msgid "Delay after unloading" -msgstr "Задержка после выгрузки" - -msgid "" -"Time to wait after the filament is unloaded. May help to get reliable " -"toolchanges with flexible materials that may need more time to shrink to " -"original dimensions." -msgstr "" -"Время ожидания после выгрузки прутка. Это может помочь вам легко сменить " -"сопло при печати гибкими материалами, которым требуется больше времени, " -"чтобы вернуться к своим первоначальным размерам." - -msgid "Number of cooling moves" -msgstr "Количество охлаждающих движений" - -msgid "" -"Filament is cooled by being moved back and forth in the cooling tubes. " -"Specify desired number of these moves." -msgstr "" -"Пруток охлаждается в охлаждающих трубках путём перемещения назад и вперёд. " -"Укажите желаемое количество таких движений." - -msgid "Speed of the first cooling move" -msgstr "Скорость первого охлаждающего движения" - -msgid "Cooling moves are gradually accelerating beginning at this speed." -msgstr "Охлаждающие движения постепенно ускоряются, начиная с этой скорости." - -msgid "Minimal purge on wipe tower" -msgstr "Мин. объём сброса на черновой башне" - -msgid "" -"After a tool change, the exact position of the newly loaded filament inside " -"the nozzle may not be known, and the filament pressure is likely not yet " -"stable. Before purging the print head into an infill or a sacrificial " -"object, Slic3r will always prime this amount of material into the wipe tower " -"to produce successive infill or sacrificial object extrusions reliably." -msgstr "" -"После смены инструмента, точное положение вновь загруженного прутка внутри " -"него может быть неизвестно, и давление прутка, вероятно, ещё не стабильно. " -"Перед тем, как очистить печатающую головку в заполнение или в «жертвенную» " -"модель Slic3r всегда будет выдавливать это количество материала на черновую " -"башню, чтобы обеспечить надёжную печать заполнения или «жертвенной» модели." - -msgid "Speed of the last cooling move" -msgstr "Скорость последнего охлаждающего движения" - -msgid "Cooling moves are gradually accelerating towards this speed." -msgstr "Охлаждающие движения постепенно ускоряют до этой скорости." - -msgid "" -"Time for the printer firmware (or the Multi Material Unit 2.0) to load a new " -"filament during a tool change (when executing the T code). This time is " -"added to the total print time by the G-code time estimator." -msgstr "" -"Время за которое прошивка принтера (или Multi Material Unit 2.0) выгружает " -"пруток во время смены инструмента (при выполнении кода Т). Это время " -"добавляется к общему времени печати с помощью алгоритма оценки времени " -"выполнения G-кода." - -msgid "Ramming parameters" -msgstr "Параметры рэмминга" - -msgid "" -"This string is edited by RammingDialog and contains ramming specific " -"parameters." -msgstr "" -"Эта строка редактируется диалоговым окном рэмминга и содержит его конкретные " -"параметры." - -msgid "" -"Time for the printer firmware (or the Multi Material Unit 2.0) to unload a " -"filament during a tool change (when executing the T code). This time is " -"added to the total print time by the G-code time estimator." -msgstr "" -"Время за которое прошивка принтера (или Multi Material Unit 2.0) выгружает " -"пруток во время смены инструмента (при выполнении кода Т). Это время " -"добавляется к общему времени печати с помощью алгоритма оценки времени " -"выполнения G-кода." - -msgid "Enable ramming for multitool setups" -msgstr "Включить рэмминг для мультиинструментальных устройств" - -msgid "" -"Perform ramming when using multitool printer (i.e. when the 'Single Extruder " -"Multimaterial' in Printer Settings is unchecked). When checked, a small " -"amount of filament is rapidly extruded on the wipe tower just before the " -"toolchange. This option is only used when the wipe tower is enabled." -msgstr "" -"Выполнять рэмминг при использовании мультиинструментального принтера (т. е. " -"когда в настройках принтера снят флажок «Мультиматериальный одиночный " -"экструдер»). При включении этой опции, небольшое количество материала быстро " -"выдавливается на черновую башню непосредственно перед сменой инструмента. " -"Эта опция используется только в том случае, если включена черновая башня." - -msgid "Multitool ramming volume" -msgstr "Объём рэмминга мультиинструмента" - -msgid "The volume to be rammed before the toolchange." -msgstr "Объём рэмминга перед сменой инструмента." - -msgid "Multitool ramming flow" -msgstr "Поток рэмминга мультиинструмента" - -msgid "Flow used for ramming the filament before the toolchange." -msgstr "Поток рэмминга пластиковой нити перед сменой инструмента." +"Введите процент усадки пластиковой нити, которую получит она после охлаждения (пишите 94%, если вы намерили 94 мм, вместо 100 мм). Для компенсации усадки деталь будет отмасштабированна по оси XY. При этом учитывается только пластиковая нить, используемая для печати внешнего периметра.\n" +"Убедитесь, что между моделями достаточно места, так как эта компенсация выполняется после проверок." msgid "Density" msgstr "Плотность" @@ -8661,32 +7138,20 @@ msgstr "Тип материала пластиковой нити." msgid "Soluble material" msgstr "Растворимый материал" -msgid "" -"Soluble material is commonly used to print support and support interface" -msgstr "" -"Растворимый материал обычно используется для печати поддержки и связующего " -"слоя поддержки." +msgid "Soluble material is commonly used to print support and support interface" +msgstr "Растворимый материал обычно используется для печати поддержки и связующего слоя поддержки." msgid "Support material" msgstr "Поддержка" -msgid "" -"Support material is commonly used to print support and support interface" -msgstr "" -"«Материал для поддержки» обычно используется для печати поддержки и " -"связующего слоя поддержки." +msgid "Support material is commonly used to print support and support interface" +msgstr "«Материал для поддержки» обычно используется для печати поддержки и связующего слоя поддержки." -msgid "Softening temperature" -msgstr "Температура размягчения" +msgid "Temperature of vitrificaiton" +msgstr "Температура стеклования" -msgid "" -"The material softens at this temperature, so when the bed temperature is " -"equal to or greater than it, it's highly recommended to open the front door " -"and/or remove the upper glass to avoid cloggings." -msgstr "" -"При этой температуре материал размягчается, поэтому, когда температура стола " -"равна или превышает её, настоятельно рекомендуется открыть переднюю дверцу и/" -"или верхнюю крышку принтера, чтобы избежать засорения сопла." +msgid "Material becomes soft at this temperature. Thus the heatbed cannot be hotter than this tempature" +msgstr "При этой температуре материал становится мягким. Таким образом, подогреваемый стол не может быть горячее этой температуры." msgid "Price" msgstr "Стоимость" @@ -8709,21 +7174,14 @@ msgstr "(Не указано)" msgid "Infill direction" msgstr "Угол печати заполнения" -msgid "" -"Angle for sparse infill pattern, which controls the start or main direction " -"of line" -msgstr "" -"Базовый угол для ориентации шаблона заполнения, который определяет начало " -"или основное направление линий." +msgid "Angle for sparse infill pattern, which controls the start or main direction of line" +msgstr "Базовый угол для ориентации шаблона заполнения, который определяет начало или основное направление линий." msgid "Sparse infill density" msgstr "Плотность заполнения" -#, fuzzy, c-format msgid "Density of internal sparse infill, 100% means solid throughout" -msgstr "" -"Плотность внутреннего заполнения, выраженная в процентах. 100% означает " -"сплошное заполнение." +msgstr "Плотность внутреннего заполнения, выраженная в процентах. 100% означает сплошное заполнение." msgid "Sparse infill pattern" msgstr "Шаблон заполнения" @@ -8765,27 +7223,13 @@ msgid "Sparse infill anchor length" msgstr "Длина привязок разреженного заполнения" msgid "" -"Connect an infill line to an internal perimeter with a short segment of an " -"additional perimeter. If expressed as percentage (example: 15%) it is " -"calculated over infill extrusion width. Slic3r tries to connect two close " -"infill lines to a short perimeter segment. If no such perimeter segment " -"shorter than infill_anchor_max is found, the infill line is connected to a " -"perimeter segment at just one side and the length of the perimeter segment " -"taken is limited to this parameter, but no longer than anchor_length_max. \n" -"Set this parameter to zero to disable anchoring perimeters connected to a " -"single infill line." +"Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Slic3r tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one " +"side and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max. \n" +"Set this parameter to zero to disable anchoring perimeters connected to a single infill line." msgstr "" -"Соединять линию заполнения с внутренним периметром с помощью короткого " -"отрезка дополнительного периметра (привязок). Если выражено в процентах, то " -"она вычисляется по ширине экструзии заполнения. Программа пытается соединить " -"две ближайшие линии заполнения с коротким отрезком периметра. Если не " -"найдено такого отрезка периметра короче «Максимальной длины привязок " -"разреженного заполнения» (anchor_length_max), то линия заполнения " -"соединяется с отрезком периметра только с одной стороны, а длина отрезка " -"периметра ограничена этим параметром, но не больше «Максимальной длины " -"привязок разреженного заполнения» (anchor_length_max). \n" -"Установите этот параметр равным нулю для отключения привязок периметров, " -"соединённых с одной линии заполнения." +"Соединять линию заполнения с внутренним периметром с помощью короткого отрезка дополнительного периметра (привязок). Если выражено в процентах, то она вычисляется по ширине экструзии заполнения. Программа пытается соединить две ближайшие линии заполнения с коротким отрезком периметра. Если не найдено такого отрезка периметра короче «Максимальной длины привязок разреженного " +"заполнения» (anchor_length_max), то линия заполнения соединяется с отрезком периметра только с одной стороны, а длина отрезка периметра ограничена этим параметром, но не больше «Максимальной длины привязок разреженного заполнения» (anchor_length_max). \n" +"Установите этот параметр равным нулю для отключения привязок периметров, соединённых с одной линии заполнения." msgid "0 (no open anchors)" msgstr "0 (нет открытых привязок)" @@ -8797,26 +7241,13 @@ msgid "Maximum length of the infill anchor" msgstr "Максимальная длина привязок разреженного заполнения" msgid "" -"Connect an infill line to an internal perimeter with a short segment of an " -"additional perimeter. If expressed as percentage (example: 15%) it is " -"calculated over infill extrusion width. Slic3r tries to connect two close " -"infill lines to a short perimeter segment. If no such perimeter segment " -"shorter than this parameter is found, the infill line is connected to a " -"perimeter segment at just one side and the length of the perimeter segment " -"taken is limited to infill_anchor, but no longer than this parameter. \n" -"If set to 0, the old algorithm for infill connection will be used, it should " -"create the same result as with 1000 & 0." +"Connect an infill line to an internal perimeter with a short segment of an additional perimeter. If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Slic3r tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side " +"and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter. \n" +"If set to 0, the old algorithm for infill connection will be used, it should create the same result as with 1000 & 0." msgstr "" -"Соединять линию заполнения с внутренним периметром с помощью короткого " -"отрезка дополнительного периметра (привязок). Если выражено в процентах, то " -"она вычисляется по ширине экструзии заполнения. Slic3r пытается соединить " -"две ближайшие линии заполнения с коротким отрезком периметра. Если не " -"найдено такого отрезка периметра короче этого параметра, линия заполнения " -"соединяется с отрезком периметра только с одной стороны, а длина отрезка " -"периметра ограничена значением «Длина привязок разреженного " -"заполнения» (infill_anchor), но не больше этого параметра. \n" -"Если установить 0, то будет использоваться старый алгоритм для соединения " -"заполнения, который даёт такой же результат, как и при значениях 1000 и 0." +"Соединять линию заполнения с внутренним периметром с помощью короткого отрезка дополнительного периметра (привязок). Если выражено в процентах, то она вычисляется по ширине экструзии заполнения. Slic3r пытается соединить две ближайшие линии заполнения с коротким отрезком периметра. Если не найдено такого отрезка периметра короче этого параметра, линия заполнения соединяется с отрезком периметра " +"только с одной стороны, а длина отрезка периметра ограничена значением «Длина привязок разреженного заполнения» (infill_anchor), но не больше этого параметра. \n" +"Если установить 0, то будет использоваться старый алгоритм для соединения заполнения, который даёт такой же результат, как и при значениях 1000 и 0." msgid "0 (Simple connect)" msgstr "0 (без привязок)" @@ -8830,71 +7261,38 @@ msgstr "Ускорение на внутренних периметрах." msgid "Acceleration of travel moves" msgstr "Ускорение холостого перемещения." -msgid "" -"Acceleration of top surface infill. Using a lower value may improve top " -"surface quality" -msgstr "" -"Ускорение на верхней поверхности. Использование меньшего значения может " -"улучшить качество верхней поверхности." +msgid "Acceleration of top surface infill. Using a lower value may improve top surface quality" +msgstr "Ускорение на верхней поверхности. Использование меньшего значения может улучшить качество верхней поверхности." msgid "Acceleration of outer wall. Using a lower value can improve quality" -msgstr "" -"Ускорение на внешнем периметре. Использование более низкого значения может " -"улучшить качество." +msgstr "Ускорение на внешнем периметре. Использование более низкого значения может улучшить качество." -msgid "" -"Acceleration of bridges. If the value is expressed as a percentage (e.g. " -"50%), it will be calculated based on the outer wall acceleration." -msgstr "" -"Ускорение на мостах. Если задано в процентах, то значение вычисляться " -"относительно ускорения внешнего периметра." +msgid "Acceleration of bridges. If the value is expressed as a percentage (e.g. 50%), it will be calculated based on the outer wall acceleration." +msgstr "Ускорение на мостах. Если задано в процентах, то значение вычисляться относительно ускорения внешнего периметра." msgid "mm/s² or %" msgstr "мм/с² или %" -msgid "" -"Acceleration of sparse infill. If the value is expressed as a percentage (e." -"g. 100%), it will be calculated based on the default acceleration." -msgstr "" -"Ускорение на разреженном заполнении. Если задано в процентах, то значение " -"вычисляться относительно ускорения по умолчанию." +msgid "Acceleration of sparse infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration." +msgstr "Ускорение на разреженном заполнении. Если задано в процентах, то значение вычисляться относительно ускорения по умолчанию." -msgid "" -"Acceleration of internal solid infill. If the value is expressed as a " -"percentage (e.g. 100%), it will be calculated based on the default " -"acceleration." -msgstr "" -"Ускорение на внутреннем сплошном заполнении. Если задано в процентах, то " -"значение вычисляться относительно ускорения по умолчанию." +msgid "Acceleration of internal solid infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration." +msgstr "Ускорение на внутреннем сплошном заполнении. Если задано в процентах, то значение вычисляться относительно ускорения по умолчанию." -msgid "" -"Acceleration of initial layer. Using a lower value can improve build plate " -"adhesive" -msgstr "" -"Ускорение на первом слое. Использование более низкого значения может " -"улучшить адгезию к столу." +msgid "Acceleration of initial layer. Using a lower value can improve build plate adhensive" +msgstr "Ускорение на первом слое. Использование более низкого значения может улучшить адгезию к столу." msgid "Enable accel_to_decel" msgstr "Вкл. ограничение ускорения зигзагов" msgid "Klipper's max_accel_to_decel will be adjusted automatically" -msgstr "" -"Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) будет " -"скорректировано автоматически" +msgstr "Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) будет скорректировано автоматически" msgid "accel_to_decel" msgstr "ограничение ускорение зигзагов" -#, c-format, boost-format -msgid "" -"Klipper's max_accel_to_decel will be adjusted to this %% of acceleration" -msgstr "" -"Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) будет " -"скорректировано на данное ускорение: %%\"" - -#, c-format, boost-format -msgid "%%" -msgstr "%%" +msgid "Klipper's max_accel_to_decel will be adjusted to this % of acceleration" +msgstr "Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) будет скорректировано на данный процент ускорения" msgid "Jerk of outer walls" msgstr "Рывок для внешних периметров." @@ -8914,22 +7312,14 @@ msgstr "Рывок для первого слоя." msgid "Jerk for travel" msgstr "Рывок при перемещении." -msgid "" -"Line width of initial layer. If expressed as a %, it will be computed over " -"the nozzle diameter." -msgstr "" -"Ширина экструзии для первого слоя. Если задано в процентах, то значение " -"вычисляться относительно диаметра сопла." +msgid "Line width of initial layer. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для первого слоя. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Initial layer height" msgstr "Высота первого слоя" -msgid "" -"Height of initial layer. Making initial layer height to be thick slightly " -"can improve build plate adhension" -msgstr "" -"Высота первого слоя. Незначительное увеличение толщины первого слоя может " -"улучшить сцепление со столом." +msgid "Height of initial layer. Making initial layer height to be thick slightly can improve build plate adhension" +msgstr "Высота первого слоя. Незначительное увеличение толщины первого слоя может улучшить сцепление со столом." msgid "Speed of initial layer except the solid infill part" msgstr "Скорость печати первого слоя, кроме сплошного заполнения." @@ -8949,59 +7339,35 @@ msgstr "Скорость перемещения на первом слое." msgid "Number of slow layers" msgstr "Количество медленных слоёв" -msgid "" -"The first few layers are printed slower than normal. The speed is gradually " -"increased in a linear fashion over the specified number of layers." -msgstr "" -"Первые несколько слоёв печатаются медленнее, чем обычно. Скорость постепенно " -"линейно увеличивается в течение заданного количества слоёв." +msgid "The first few layers are printed slower than normal. The speed is gradually increased in a linear fashion over the specified number of layers." +msgstr "Первые несколько слоёв печатаются медленнее, чем обычно. Скорость постепенно линейно увеличивается в течение заданного количества слоёв." msgid "Initial layer nozzle temperature" msgstr "Температура сопла на первом слое" msgid "Nozzle temperature to print initial layer when using this filament" -msgstr "" -"Температура сопла для печати первого слоя при использовании данной " -"пластиковой нити." +msgstr "Температура сопла для печати первого слоя при использовании данной пластиковой нити." msgid "Full fan speed at layer" msgstr "Полная скорость вентилятора на слое" -msgid "" -"Fan speed will be ramped up linearly from zero at layer " -"\"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer" -"\". \"full_fan_speed_layer\" will be ignored if lower than " -"\"close_fan_the_first_x_layers\", in which case the fan will be running at " -"maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." -msgstr "" -"Скорость вентилятора будет нарастать линейно от нуля на слое " -"\"close_fan_the_first_x_layers\" до максимума на слое \"full_fan_speed_layer" -"\". Значение \"full_fan_speed_layer\" будет игнорироваться, если оно меньше " -"значения \"close_fan_the_first_x_layers\", в этом случае вентилятор будет " -"работать на максимально допустимой скорости на слое " -"\"close_fan_the_first_x_layers\" + 1." +msgid "Fan speed will be ramped up linearly from zero at layer \"close_fan_the_first_x_layers\" to maximum at layer \"full_fan_speed_layer\". \"full_fan_speed_layer\" will be ignored if lower than \"close_fan_the_first_x_layers\", in which case the fan will be running at maximum allowed speed at layer \"close_fan_the_first_x_layers\" + 1." +msgstr "Скорость вентилятора будет нарастать линейно от нуля на слое \"close_fan_the_first_x_layers\" до максимума на слое \"full_fan_speed_layer\". Значение \"full_fan_speed_layer\" будет игнорироваться, если оно меньше значения \"close_fan_the_first_x_layers\", в этом случае вентилятор будет работать на максимально допустимой скорости на слое \"close_fan_the_first_x_layers\" + 1." msgid "Support interface fan speed" msgstr "Скорость вентилятора на связующем слое" msgid "" -"This fan speed is enforced during all support interfaces, to be able to " -"weaken their bonding with a high fan speed.\n" +"This fan speed is enforced during all support interfaces, to be able to weaken their bonding with a high fan speed.\n" "Set to -1 to disable this override.\n" "Can only be overriden by disable_fan_first_layers." msgstr "" -"Скорость, применяемая ко всем связующим слоях, чтобы высокой скоростью " -"вентилятора ослабить сцепление между слоями.\n" +"Скорость, применяемая ко всем связующим слоях, чтобы высокой скоростью вентилятора ослабить сцепление между слоями.\n" "Установите значение -1, чтобы запретить переопределять этот параметр.\n" "Может быть отменено только командой disable_fan_first_layers." -msgid "" -"Randomly jitter while printing the wall, so that the surface has a rough " -"look. This setting controls the fuzzy position" -msgstr "" -"Случайное дрожание сопла при печати внешнего периметра для создания эффекта " -"шероховатой поверхности. Эта настройка определяет положение нечетной " -"оболочки." +msgid "Randomly jitter while printing the wall, so that the surface has a rough look. This setting controls the fuzzy position" +msgstr "Случайное дрожание сопла при печати внешнего периметра для создания эффекта шероховатой поверхности. Эта настройка определяет положение нечетной оболочки." msgid "None" msgstr "Нет" @@ -9018,22 +7384,14 @@ msgstr "Все периметры" msgid "Fuzzy skin thickness" msgstr "Толщина нечёткой оболочки" -msgid "" -"The width within which to jitter. It's adversed to be below outer wall line " -"width" -msgstr "" -"Ширина, в пределах которой будет происходить дрожание. Желательно, чтобы она " -"была меньше ширины линии внешнего периметра." +msgid "The width within which to jitter. It's adversed to be below outer wall line width" +msgstr "Ширина, в пределах которой будет происходить дрожание. Желательно, чтобы она была меньше ширины линии внешнего периметра." msgid "Fuzzy skin point distance" msgstr "Расстояние «дрожания» при печати нечёткой оболочки" -msgid "" -"The average diatance between the random points introducded on each line " -"segment" -msgstr "" -"Среднее расстояние между случайно вставленными точками при генерации " -"нечётной оболочки." +msgid "The average diatance between the random points introducded on each line segment" +msgstr "Среднее расстояние между случайно вставленными точками при генерации нечётной оболочки." msgid "Filter out tiny gaps" msgstr "Игнорировать небольшие пробелы" @@ -9041,52 +7399,35 @@ msgstr "Игнорировать небольшие пробелы" msgid "Layers and Perimeters" msgstr "Слои и периметры" -msgid "Filter out gaps smaller than the threshold specified" -msgstr "Небольшие промежутки меньше указанного порога не будут заполняться." +msgid "Filter out gaps smaller than the threshold specified. This setting won't affect top/bottom layers" +msgstr "Небольшие промежутки меньше указанного порога не будут заполняться. Этот параметр не влияет на верхнюю/нижнюю поверхность." -msgid "" -"Speed of gap infill. Gap usually has irregular line width and should be " -"printed more slowly" -msgstr "" -"Скорость заполнения пробелов. Пробелы обычно имеют неравномерную ширину " -"линии и должен печататься медленнее." +msgid "Speed of gap infill. Gap usually has irregular line width and should be printed more slowly" +msgstr "Скорость заполнения пробелов. Пробелы обычно имеют неравномерную ширину линии и должен печататься медленнее." msgid "Arc fitting" msgstr "Поддержка движения по дуге окружности" -msgid "" -"Enable this to get a G-code file which has G2 and G3 moves. And the fitting " -"tolerance is same with resolution" -msgstr "" -"Включите, если хотите использовать в G-коде команды перемещения по дуге " -"окружности G2/G3. Значение допуска траектории такое же как разрешение G-кода " -"выше." +msgid "Enable this to get a G-code file which has G2 and G3 moves. And the fitting tolerance is same with resolution" +msgstr "Включите, если хотите использовать в G-коде команды перемещения по дуге окружности G2/G3. Значение допуска траектории такое же как разрешение G-кода выше." msgid "Add line number" msgstr "Добавить номер строки" msgid "Enable this to add line number(Nx) at the beginning of each G-Code line" -msgstr "" -"При включении, в начало каждой строки G-кода, будет добавляться номер строки " -"(Nx)." +msgstr "При включении, в начало каждой строки G-кода, будет добавляться номер строки (Nx)." msgid "Scan first layer" -msgstr "Сканировать первый слой" +msgstr "Проверка первого слоя" -msgid "" -"Enable this to enable the camera on printer to check the quality of first " -"layer" +msgid "Enable this to enable the camera on printer to check the quality of first layer" msgstr "При включении, камера принтера будет проверять качество первого слоя." msgid "Nozzle type" msgstr "Тип сопла" -msgid "" -"The metallic material of nozzle. This determines the abrasive resistance of " -"nozzle, and what kind of filament can be printed" -msgstr "" -"Материал сопла. Определяет абразивную стойкость сопла, а также то, каким " -"материалом можно печатать." +msgid "The metallic material of nozzle. This determines the abrasive resistance of nozzle, and what kind of filament can be printed" +msgstr "Материал сопла. Определяет абразивную стойкость сопла, а также то, каким материалом можно печатать." msgid "Undefine" msgstr "Не задано" @@ -9103,68 +7444,24 @@ msgstr "Латунь" msgid "Nozzle HRC" msgstr "Твердость сопла (HRC)" -msgid "" -"The nozzle's hardness. Zero means no checking for nozzle's hardness during " -"slicing." -msgstr "" -"Твёрдость сопел. 0 - отключение контроля сопел на твёрдость во время нарезки." +msgid "The nozzle's hardness. Zero means no checking for nozzle's hardness during slicing." +msgstr "Твёрдость сопел. 0 - отключение контроля сопел на твёрдость во время нарезки." msgid "HRC" msgstr "HRC" -msgid "Printer structure" -msgstr "Кинематика принтера" - -msgid "The physical arrangement and components of a printing device" -msgstr "Конструкция физического принтера" - -msgid "CoreXY" -msgstr "CoreXY" - -msgid "I3" -msgstr "I3" - -msgid "Hbot" -msgstr "Hbot" - -msgid "Delta" -msgstr "Delta" - -msgid "Best object position" -msgstr "Наилучшее расположение модели" - -msgid "Best auto arranging position in range [0,1] w.r.t. bed shape." -msgstr "" -"Наилучшее расположение модели при авторасстановке в диапазоне [0,1] " -"относительно формы стола." +msgid "Enable this option if machine has auxiliary part cooling fan" +msgstr "Включите, если в принтере имеет вспомогательный вентилятор для охлаждения моделей." msgid "" -"Enable this option if machine has auxiliary part cooling fan. G-code " -"command: M106 P2 S(0-255)." -msgstr "" -"Если в принтере имеет вспомогательный вентилятор для охлаждения моделей, " -"можете включить эту опцию. \n" -"G-код команда: M106 P2 S(0-255)." - -msgid "" -"Start the fan this number of seconds earlier than its target start time (you " -"can use fractional seconds). It assumes infinite acceleration for this time " -"estimation, and will only take into account G1 and G0 moves (arc fitting is " -"unsupported).\n" -"It won't move fan comands from custom gcodes (they act as a sort of " -"'barrier').\n" -"It won't move fan comands into the start gcode if the 'only custom start " -"gcode' is activated.\n" +"Start the fan this number of seconds earlier than its target start time (you can use fractional seconds). It assumes infinite acceleration for this time estimation, and will only take into account G1 and G0 moves (arc fitting is unsupported).\n" +"It won't move fan comands from custom gcodes (they act as a sort of 'barrier').\n" +"It won't move fan comands into the start gcode if the 'only custom start gcode' is activated.\n" "Use 0 to deactivate." msgstr "" -"Запуск вентилятора на указанное количество секунд раньше целевого времени " -"запуска (поддерживаются доли секунды). При этом предполагается бесконечное " -"ускорение для оценки этого времени, и учёт только перемещений G1 и G0 " -"(Поддержка движения по дуге окружности не поддерживается).\n" -"Это не приведёт к сдвигу команд вентилятора из пользовательских G-кодов (они " -"действуют как своего рода барьер).\n" -"Это не приведёт к сдвигу команд вентилятора в стартовом G-коде, если " -"активировано «только пользовательский стартовый G-код».\n" +"Запуск вентилятора на указанное количество секунд раньше целевого времени запуска (поддерживаются доли секунды). При этом предполагается бесконечное ускорение для оценки этого времени, и учёт только перемещений G1 и G0 (Поддержка движения по дуге окружности не поддерживается).\n" +"Это не приведёт к сдвигу команд вентилятора из пользовательских G-кодов (они действуют как своего рода барьер).\n" +"Это не приведёт к сдвигу команд вентилятора в стартовом G-коде, если активировано «только пользовательский стартовый G-код».\n" "Установите 0 для отключения." msgid "Only overhangs" @@ -9177,50 +7474,13 @@ msgid "Fan kick-start time" msgstr "Продолжительность принудительного запуска вентилятора" msgid "" -"Emit a max fan speed command for this amount of seconds before reducing to " -"target speed to kick-start the cooling fan.\n" -"This is useful for fans where a low PWM/power may be insufficient to get the " -"fan started spinning from a stop, or to get the fan up to speed faster.\n" +"Emit a max fan speed command for this amount of seconds before reducing to target speed to kick-start the cooling fan.\n" +"This is useful for fans where a low PWM/power may be insufficient to get the fan started spinning from a stop, or to get the fan up to speed faster.\n" "Set to 0 to deactivate." msgstr "" -"Время принудительного запуска (kick-start) вентилятора на максимальной " -"скорости, после чего скорость снижается до целевой. Это необходимо для " -"вентиляторов у которых низкое значение уровня ШИМ/мощности может быть " -"недостаточен для запуска вентилятора после остановки или для более быстрого " -"увеличения скорости его вращения.\n" +"Время принудительного запуска (kick-start) вентилятора на максимальной скорости, после чего скорость снижается до целевой. Это необходимо для вентиляторов у которых низкое значение уровня ШИМ/мощности может быть недостаточен для запуска вентилятора после остановки или для более быстрого увеличения скорости его вращения.\n" "Установите 0 для отключения." -msgid "Time cost" -msgstr "Цена печати" - -msgid "The printer cost per hour" -msgstr "Стоимость печати в час." - -msgid "money/h" -msgstr "цена/ч" - -msgid "Support control chamber temperature" -msgstr "Контроль температуры в термокамере" - -msgid "" -"This option is enabled if machine support controlling chamber temperature\n" -"G-code command: M141 S(0-255)" -msgstr "" -"Если принтер поддерживает контроль температуры в камере, включите эту " -"опцию.\n" -"G-код команда: M141 S(0-255)" - -msgid "Support air filtration" -msgstr "Фильтрация выдуваемого воздуха" - -msgid "" -"Enable this if printer support air filtration\n" -"G-code command: M106 P3 S(0-255)" -msgstr "" -"Если принтер поддерживает фильтрацию выдуваемого воздуха, включите эту " -"опцию. \n" -"G-код команда: M106 P3 S(0-255)" - msgid "G-code flavor" msgstr "Тип G-кода" @@ -9233,67 +7493,40 @@ msgstr "Klipper" msgid "Label objects" msgstr "Название моделей" -msgid "" -"Enable this to add comments into the G-Code labeling print moves with what " -"object they belong to, which is useful for the Octoprint CancelObject " -"plugin. This settings is NOT compatible with Single Extruder Multi Material " -"setup and Wipe into Object / Wipe into Infill." -msgstr "" -"Включите эту опцию, чтобы добавить комментарии в G-код с указанием того, к " -"какой модели он принадлежит, что полезно для плагина Octoprint CancelObject. " -"Эта настройка не совместима с настройкой «Мультиматериальный одиночный " -"экструдер» и «Очистка в модель» / «Очистка в заполнение модели»." +msgid "Enable this to add comments into the G-Code labeling print moves with what object they belong to, which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill." +msgstr "Включите эту опцию, чтобы добавить комментарии в G-код с указанием того, к какой модели он принадлежит, что полезно для плагина Octoprint CancelObject. Эта настройка не совместима с настройкой «Мультиматериальный одиночный экструдер» и «Очистка в модель» / «Очистка в заполнение модели»." msgid "Exclude objects" msgstr "Исключить модели" msgid "Enable this option to add EXCLUDE OBJECT command in g-code" -msgstr "" -"Включите эту опцию, чтобы добавить команду EXCLUDE OBJECT (исключения " -"моделей) в G-код." +msgstr "Включите эту опцию, чтобы добавить команду EXCLUDE OBJECT (исключения моделей) в G-код." msgid "Verbose G-code" msgstr "Подробный G-код" -msgid "" -"Enable this to get a commented G-code file, with each line explained by a " -"descriptive text. If you print from SD card, the additional weight of the " -"file could make your firmware slow down." -msgstr "" -"Включите эту опцию, чтобы в каждой строке файла G-кода, присутствовал " -"комментарий с поясняющим текстом. При печати с SD-карты, скорость чтение " -"данных вашей прошивкой может снизится за счёт увеличения размера файла." +msgid "Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down." +msgstr "Включите эту опцию, чтобы в каждой строке файла G-кода, присутствовал комментарий с поясняющим текстом. При печати с SD-карты, скорость чтение данных вашей прошивкой может снизится за счёт увеличения размера файла." msgid "Infill combination" msgstr "Комбинированное заполнение" -msgid "" -"Automatically Combine sparse infill of several layers to print together to " -"reduce time. Wall is still printed with original layer height." +msgid "Automatically Combine sparse infill of several layers to print together to reduce time. Wall is still printed with original layer height." msgstr "" -"Для сокращения времени печати есть возможность печатать заполнение не на " -"каждом слое, а на двух слоях сразу. \n" +"Для сокращения времени печати есть возможность печатать заполнение не на каждом слое, а на двух слоях сразу. \n" "Периметры по-прежнему печатаются с исходной высотой слоя." msgid "Filament to print internal sparse infill." msgstr "Пластиковая нить для печати заполнения." -msgid "" -"Line width of internal sparse infill. If expressed as a %, it will be " -"computed over the nozzle diameter." -msgstr "" -"Ширина экструзии для заполнения. Если задано в процентах, то значение " -"вычисляться относительно диаметра сопла." +msgid "Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для заполнения. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Infill/Wall overlap" msgstr "Перекрытие линий заполнения с линиями периметра" -msgid "" -"Infill area is enlarged slightly to overlap with wall for better bonding. " -"The percentage value is relative to line width of sparse infill" -msgstr "" -"Параметр указывает на сколько процентов заполнение будет перекрываться с " -"периметром для лучшего соединения друг с другом." +msgid "Infill area is enlarged slightly to overlap with wall for better bonding. The percentage value is relative to line width of sparse infill" +msgstr "Параметр указывает на сколько процентов заполнение будет перекрываться с периметром для лучшего соединения друг с другом." msgid "Speed of internal sparse infill" msgstr "Скорость заполнения" @@ -9301,26 +7534,14 @@ msgstr "Скорость заполнения" msgid "Interface shells" msgstr "Связующие оболочки" -msgid "" -"Force the generation of solid shells between adjacent materials/volumes. " -"Useful for multi-extruder prints with translucent materials or manual " -"soluble support material" -msgstr "" -"Принудительное создание замкнутых (сплошных) оболочек между смежными " -"материалами/объёмами. Полезно для многоэкструдерных принтеров при печати " -"полупрозрачными материалами или растворимой поддержкой. Помогает избежать " -"диффузию материалов." +msgid "Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material" +msgstr "Принудительное создание замкнутых (сплошных) оболочек между смежными материалами/объёмами. Полезно для многоэкструдерных принтеров при печати полупрозрачными материалами или растворимой поддержкой. Помогает избежать диффузию материалов." msgid "Ironing Type" msgstr "Тип разглаживания" -msgid "" -"Ironing is using small flow to print on same height of surface again to make " -"flat surface more smooth. This setting controls which layer being ironed" -msgstr "" -"При разглаживании сопло выполняет вторую фазу заполнения на том же слое (с " -"небольшим потоком), чтобы заполнить отверстия и сгладить выступающие части " -"пластика. Этот параметр контролирует, какой слой необходимо сгладить." +msgid "Ironing is using small flow to print on same height of surface again to make flat surface more smooth. This setting controls which layer being ironed" +msgstr "При разглаживании сопло выполняет вторую фазу заполнения на том же слое (с небольшим потоком), чтобы заполнить отверстия и сгладить выступающие части пластика. Этот параметр контролирует, какой слой необходимо сгладить." msgid "No ironing" msgstr "Без разглаживания" @@ -9337,18 +7558,11 @@ msgstr "Все сплошные поверхности" msgid "Ironing Pattern" msgstr "Шаблон разглаживания" -msgid "The pattern that will be used when ironing" -msgstr "Шаблон по которому будет производиться разглаживание." - msgid "Ironing flow" msgstr "Поток" -msgid "" -"The amount of material to extrude during ironing. Relative to flow of normal " -"layer height. Too high value results in overextrusion on the surface" -msgstr "" -"Количество материала, которое необходимо выдавить во время разглаживания " -"относительно потока при нормальной высоте слоя." +msgid "The amount of material to extrude during ironing. Relative to flow of normal layer height. Too high value results in overextrusion on the surface" +msgstr "Количество материала, которое необходимо выдавить во время разглаживания относительно потока при нормальной высоте слоя." msgid "Ironing line spacing" msgstr "Расстояние между линиями разглаживания" @@ -9362,36 +7576,17 @@ msgstr "Скорость разглаживания" msgid "Print speed of ironing lines" msgstr "Скорость разглаживания" -msgid "Ironing angle" -msgstr "Угол разглаживания" - -msgid "" -"The angle ironing is done at. A negative number disables this function and " -"uses the default method." -msgstr "" -"Выбор угла разглаживания. Отрицательное число отключает эту функцию и " -"использует метод по умолчанию." - msgid "This gcode part is inserted at every layer change after lift z" -msgstr "" -"Этот G-код вставляется при каждой смене слоя, сразу после перемещения оси Z." +msgstr "Этот G-код вставляется при каждой смене слоя, сразу после перемещения оси Z." msgid "Supports silent mode" msgstr "Поддержка тихого режима" -msgid "" -"Whether the machine supports silent mode in which machine use lower " -"acceleration to print" -msgstr "" -"Поддержка тихого режима, в котором принтер использует меньшее ускорение " -"печати для уменьшения уровня шума." +msgid "Whether the machine supports silent mode in which machine use lower acceleration to print" +msgstr "Поддержка тихого режима, в котором принтер использует меньшее ускорение печати для уменьшения уровня шума." -msgid "" -"This G-code will be used as a code for the pause print. User can insert " -"pause G-code in gcode viewer" -msgstr "" -"Этот G-код используется для задания паузы печати. Пользователь может " -"вставить G-код паузы в просмотрщике G-кода." +msgid "This G-code will be used as a code for the pause print. User can insert pause G-code in gcode viewer" +msgstr "Этот G-код используется для задания паузы печати. Пользователь может вставить G-код паузы в просмотрщике G-кода." msgid "This G-code will be used as a custom code" msgstr "Этот G-код используется для пользовательского кода." @@ -9499,155 +7694,40 @@ msgid "Maximum acceleration for travel" msgstr "Максимальное ускорение при перемещении" msgid "Maximum acceleration for travel (M204 T), it only applies to Marlin 2" -msgstr "" -"Максимальное ускорение при перемещении (M204 T), применяемое только для " -"Marlin 2" +msgstr "Максимальное ускорение при перемещении (M204 T), применяемое только для Marlin 2" -msgid "" -"Part cooling fan speed may be increased when auto cooling is enabled. This " -"is the maximum speed limitation of part cooling fan" -msgstr "" -"Скорость вентилятора охлаждения моделей может быть увеличена, если включено " -"автоматическое охлаждение. Это максимальное ограничение скорости вентилятора " -"для охлаждения моделей." +msgid "Maximum acceleration for travel (M204 T)" +msgstr "Максимальное ускорение при перемещении (M204 T)" + +msgid "Fan speed" +msgstr "Скорость вентилятора" + +msgid "Part cooling fan speed may be increased when auto cooling is enabled. This is the maximum speed limitation of part cooling fan" +msgstr "Скорость вентилятора охлаждения моделей может быть увеличена, если включено автоматическое охлаждение. Это максимальное ограничение скорости вентилятора для охлаждения моделей." msgid "Max" msgstr "Макс." -msgid "" -"The largest printable layer height for extruder. Used tp limits the maximum " -"layer hight when enable adaptive layer height" -msgstr "" -"Это наибольшая высота печатаемого слоя для этого экструдера, которая " -"используется для ограничения функции «Переменная высота слоёв»." - -# ??? -msgid "Extrusion rate smoothing" -msgstr "Сглаживание скорости экструзии" - -msgid "" -"This parameter smooths out sudden extrusion rate changes that happen when " -"the printer transitions from printing a high flow (high speed/larger width) " -"extrusion to a lower flow (lower speed/smaller width) extrusion and vice " -"versa.\n" -"\n" -"It defines the maximum rate by which the extruded volumetric flow in mm3/sec " -"can change over time. Higher values mean higher extrusion rate changes are " -"allowed, resulting in faster speed transitions.\n" -"\n" -"A value of 0 disables the feature. \n" -"\n" -"For a high speed, high flow direct drive printer (like the Bambu lab or " -"Voron) this value is usually not needed. However it can provide some " -"marginal benefit in certain cases where feature speeds vary greatly. For " -"example, when there are aggressive slowdowns due to overhangs. In these " -"cases a high value of around 300-350mm3/s2 is recommended as this allows for " -"just enough smoothing to assist pressure advance achieve a smoother flow " -"transition.\n" -"\n" -"For slower printers without pressure advance, the value should be set much " -"lower. A value of 10-15mm3/s2 is a good starting point for direct drive " -"extruders and 5-10mm3/s2 for Bowden style. \n" -"\n" -"This feature is known as Pressure Equalizer in Prusa slicer.\n" -"\n" -"Note: this parameter disables arc fitting." -msgstr "" -"Этот параметр сглаживает резкие изменения скорости экструзии, которые " -"происходят, когда принтер переходит от печати с большим расходом (высокая " -"скорость/большая ширина) к печати с меньшим расходом (меньшая скорость/" -"меньшая ширина) и наоборот.\n" -"\n" -"Параметр задаёт максимальную скорость, с которой объёмный расход " -"экструдируемого материала может изменяться с течением времени (мм³/с). Более " -"высокие значения означают, что допускаются более высокие изменения скорости " -"экструзии, что приводит к более быстрому переключению скоростей.\n" -"\n" -"Значение 0 отключает эту функцию. \n" -"\n" -"Для высокоскоростных принтеров с прямым приводом (например, Bambu lab или " -"Voron) обычно не требуется использование данного значения. Однако в " -"некоторых случаях, когда скорость печати сильно различается, это может " -"принести некоторую дополнительную пользу. Например, когда происходят резкие " -"замедления из-за нависаний. В этих случаях рекомендуется использовать " -"высокое значение, составляющее около 300-350 мм³/с², так как это " -"обеспечивает достаточное сглаживание, помогающее прогнозированию давления " -"достичь более плавного перехода потока.\n" -"\n" -"Для более медленных принтеров, не использующих прогнозирование давления " -"(pressure advance), это значение должно быть значительно ниже. Значение " -"10-15 мм³/с² является хорошей отправной точкой для экструдеров с прямым " -"приводом и 5-10 мм³/с² для боуден экструдеров.\n" -"\n" -"В Prusa Slicer эта функция известна как «Выравнивание давления» (Pressure " -"equalizer).\n" -"\n" -"Примечание: этот параметр отключает поддержку движения по дуге окружности." - -msgid "mm³/s²" -msgstr "мм³/с²" - -msgid "Smoothing segment length" -msgstr "Длина сглаживающего сегмента" - -msgid "" -"A lower value results in smoother extrusion rate transitions. However, this " -"results in a significantly larger gcode file and more instructions for the " -"printer to process. \n" -"\n" -"Default value of 3 works well for most cases. If your printer is stuttering, " -"increase this value to reduce the number of adjustments made\n" -"\n" -"Allowed values: 1-5" -msgstr "" -"Меньшее значение приводит к более плавному изменению скорости экструзии. " -"Однако это приводит к значительному увеличению размера G-код файла и " -"увеличению количества инструкций для обработки принтером. \n" -"\n" -"Значение по умолчанию, равное 3, хорошо подходит для большинства случаев. " -"Если принтер печатает с мини-фризами, увеличьте это значение, чтобы " -"уменьшить количество выполняемых изменений.\n" -"\n" -"Допустимые значения: 1–5." +msgid "The largest printable layer height for extruder. Used tp limits the maximum layer hight when enable adaptive layer height" +msgstr "Это наибольшая высота печатаемого слоя для этого экструдера, которая используется для ограничения функции «Переменная высота слоёв»." msgid "Minimum speed for part cooling fan" msgstr "Минимальная скорость вентилятора обдува модели." -msgid "" -"Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed " -"during printing except the first several layers which is defined by no " -"cooling layers.\n" -"Please enable auxiliary_fan in printer settings to use this feature. G-code " -"command: M106 P2 S(0-255)" -msgstr "" -"Скорость вращения вспомогательного вентилятора для охлаждения моделей. Он " -"всегда будет работать с этой скоростью, за исключением первых нескольких " -"слоёв, которые обычно настроены на работу без охлаждения.\n" -"Пожалуйста, включите вспомогательный вентилятор для охлаждения моделей " -"(auxiliary_fan) в настройках принтера, чтобы использовать эту функцию. \n" -"G-код команда: M106 P2 S(0-255)." +msgid "Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed during printing except the first several layers which is defined by no cooling layers" +msgstr "Скорость вращения вспомогательного вентилятора для охлаждения моделей. Он всегда будет работать с этой скоростью, за исключением первых нескольких слоёв, которые обычно настроены на работу без охлаждения." msgid "Min" msgstr "Мин." -msgid "" -"The lowest printable layer height for extruder. Used tp limits the minimum " -"layer hight when enable adaptive layer height" -msgstr "" -"Это наименьшая высота печатаемого слоя для данного экструдера и в то же " -"время нижний предел для функции «Переменная высота слоёв»." +msgid "The lowest printable layer height for extruder. Used tp limits the minimum layer hight when enable adaptive layer height" +msgstr "Это наименьшая высота печатаемого слоя для данного экструдера и в то же время нижний предел для функции «Переменная высота слоёв»." msgid "Min print speed" msgstr "Минимальная скорость печати" -msgid "" -"The minimum printing speed for the filament when slow down for better layer " -"cooling is enabled, when printing overhangs and when feature speeds are not " -"specified explicitly." -msgstr "" -"Минимальная скорость печати для текущего прутка при включенной функции " -"«Замедлять печать для лучшего охлаждения слоёв» при печати нависаний и когда " -"скорости элементов явно не задана." +msgid "The minimum printing speed when slow down for cooling" +msgstr "Минимальная скорость печати при которой происходит сброс скорости для лучшего охлаждения." msgid "Nozzle diameter" msgstr "Диаметр сопла" @@ -9658,22 +7738,14 @@ msgstr "Диаметр сопла" msgid "Configuration notes" msgstr "Примечание конфигурации" -msgid "" -"You can put here your personal notes. This text will be added to the G-code " -"header comments." -msgstr "" -"Здесь вы можете написать свои замечания для текущего профиля. Этот текст " -"будет добавлен к комментариям в заголовок G-кода." +msgid "You can put here your personal notes. This text will be added to the G-code header comments." +msgstr "Здесь вы можете написать свои замечания для текущего профиля. Этот текст будет добавлен к комментариям в заголовок G-кода." msgid "Host Type" msgstr "Тип хоста" -msgid "" -"Slic3r can upload G-code files to a printer host. This field must contain " -"the kind of the host." -msgstr "" -"Slic3r может загружать файл G-кода на хост принтера. Это поле должно " -"содержать тип хоста." +msgid "Slic3r can upload G-code files to a printer host. This field must contain the kind of the host." +msgstr "Slic3r может загружать файл G-кода на хост принтера. Это поле должно содержать тип хоста." msgid "Nozzle volume" msgstr "Объём сопла" @@ -9681,57 +7753,6 @@ msgstr "Объём сопла" msgid "Volume of nozzle between the cutter and the end of nozzle" msgstr "Объём сопла между резцом прутка и кончиком сопла." -msgid "Cooling tube position" -msgstr "Позиция охлаждающей трубки" - -msgid "Distance of the center-point of the cooling tube from the extruder tip." -msgstr "" -"Расстояние между центральной точкой охлаждающей трубки и кончиком экструдера." - -msgid "Cooling tube length" -msgstr "Длина охлаждающей трубки" - -msgid "Length of the cooling tube to limit space for cooling moves inside it." -msgstr "" -"Длина охлаждающей трубки для ограничения перемещения при охлаждающих " -"движениях." - -msgid "High extruder current on filament swap" -msgstr "Повышение тока экструдера при замене прутка" - -msgid "" -"It may be beneficial to increase the extruder motor current during the " -"filament exchange sequence to allow for rapid ramming feed rates and to " -"overcome resistance when loading a filament with an ugly shaped tip." -msgstr "" -"Это может быть полезно для увеличения тока двигателя экструдера во время " -"замены прутка, чтобы быстро увеличить скорость подачи и преодолеть " -"сопротивление при загрузке прутка с плохой формой кончика." - -msgid "Filament parking position" -msgstr "Положение парковки прутка" - -msgid "" -"Distance of the extruder tip from the position where the filament is parked " -"when unloaded. This should match the value in printer firmware." -msgstr "" -"Расстояние от кончика экструдера до точки, где размещается пруток при " -"выгрузке. Расстояние должно соответствовать значению в прошивке принтера." - -msgid "Extra loading distance" -msgstr "Дополнительная длина загрузки" - -msgid "" -"When set to zero, the distance the filament is moved from parking position " -"during load is exactly the same as it was moved back during unload. When " -"positive, it is loaded further, if negative, the loading move is shorter " -"than unloading." -msgstr "" -"Если установлено 0, то расстояние, которое проходит пруток при перемещении " -"из положения парковки во время загрузки, точно такое же, как и при выгрузке. " -"При положительном значении, она загружается дальше; при отрицательном, ход " -"загрузки короче (по сравнению с выгрузкой)." - msgid "Start end points" msgstr "Начальные и конечные точки" @@ -9741,15 +7762,8 @@ msgstr "Начальная и конечная точки от зоны обре msgid "Reduce infill retraction" msgstr "Уменьшать отката при заполнении" -msgid "" -"Don't retract when the travel is in infill area absolutely. That means the " -"oozing can't been seen. This can reduce times of retraction for complex " -"model and save printing time, but make slicing and G-code generating slower" -msgstr "" -"Отключает откат, когда перемещения полностью совершаются в области " -"заполнения (и, таким образом, любые подтёки скорее всего будут не заметны). " -"Это поможет снизить количество откатов при печати сложной модели и " -"сэкономить время печати, но увеличит время нарезки и генерации G-кода." +msgid "Don't retract when the travel is in infill area absolutely. That means the oozing can't been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower" +msgstr "Отключает откат, когда перемещения полностью совершаются в области заполнения (и, таким образом, любые подтёки скорее всего будут не заметны). Это поможет снизить количество откатов при печати сложной модели и сэкономить время печати, но увеличит время нарезки и генерации G-кода." msgid "Enable" msgstr "Включить" @@ -9769,25 +7783,14 @@ msgstr "Изменение геометрии модели для печати msgid "Make overhang printable maximum angle" msgstr "Делать нависания пригодными для печати под максимальным углом" -msgid "" -"Maximum angle of overhangs to allow after making more steep overhangs " -"printable.90° will not change the model at all and allow any overhang, while " -"0 will replace all overhangs with conical material." -msgstr "" -"Максимальный угол нависания, получаемый после изменения геометрии крутых " -"нависаний. При 90°не происходит изменения формы модели. При 0° же, все " -"нависания заменяются материалом конической геометрии." +msgid "Maximum angle of overhangs to allow after making more steep overhangs printable.90° will not change the model at all and allow any overhang, while 0 will replace all overhangs with conical material." +msgstr "Максимальный угол нависания, получаемый после изменения геометрии крутых нависаний. При 90°не происходит изменения формы модели. При 0° же, все нависания заменяются материалом конической геометрии." msgid "Make overhang printable hole area" msgstr "Делать нависания отверстий пригодными для печати" -msgid "" -"Maximum area of a hole in the base of the model before it's filled by " -"conical material.A value of 0 will fill all the holes in the model base." -msgstr "" -"Максимальная площадь отверстия в основании модели до его заполнения " -"материалом конической геометрии. При 0 все отверстия в основании модели " -"будут заполнены." +msgid "Maximum area of a hole in the base of the model before it's filled by conical material.A value of 0 will fill all the holes in the model base." +msgstr "Максимальная площадь отверстия в основании модели до его заполнения материалом конической геометрии. При 0 все отверстия в основании модели будут заполнены." msgid "mm²" msgstr "мм²" @@ -9796,19 +7799,11 @@ msgid "Detect overhang wall" msgstr "Определять нависающие периметры" #, c-format, boost-format -msgid "" -"Detect the overhang percentage relative to line width and use different " -"speed to print. For 100%% overhang, bridge speed is used." -msgstr "" -"Определяет процент нависания относительно ширины линии и использует разную " -"скорость печати. Для 100%%-го свеса используется скорость печати мостов." +msgid "Detect the overhang percentage relative to line width and use different speed to print. For 100%% overhang, bridge speed is used." +msgstr "Определяет процент нависания относительно ширины линии и использует разную скорость печати. Для 100%%-го свеса используется скорость печати мостов." -msgid "" -"Line width of inner wall. If expressed as a %, it will be computed over the " -"nozzle diameter." -msgstr "" -"Ширина экструзии внутренних периметров. Если задано в процентах, то значение " -"вычисляться относительно диаметра сопла." +msgid "Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии внутренних периметров. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Speed of inner wall" msgstr "Скорость печати внутренних периметров." @@ -9816,18 +7811,8 @@ msgstr "Скорость печати внутренних периметров. msgid "Number of walls of every layer" msgstr "Количество периметров на каждом слое модели." -msgid "" -"If you want to process the output G-code through custom scripts, just list " -"their absolute paths here. Separate multiple scripts with a semicolon. " -"Scripts will be passed the absolute path to the G-code file as the first " -"argument, and they can access the Slic3r config settings by reading " -"environment variables." -msgstr "" -"Если вы хотите обработать выходной G-код с помощью пользовательских " -"скриптов, просто перечислите здесь абсолютные пути к ним. Разделяйте скрипты " -"точкой с запятой. Скриптам будет передан абсолютный путь к файлу G-кода в " -"качестве первого аргумента, и они смогут получить доступ к настройкам " -"конфигурации Slic3r, читая переменные окружения." +msgid "If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables." +msgstr "Если вы хотите обработать выходной G-код с помощью пользовательских скриптов, просто перечислите здесь абсолютные пути к ним. Разделяйте скрипты точкой с запятой. Скриптам будет передан абсолютный путь к файлу G-кода в качестве первого аргумента, и они смогут получить доступ к настройкам конфигурации Slic3r, читая переменные окружения." msgid "Printer notes" msgstr "Примечания к принтеру" @@ -9839,9 +7824,7 @@ msgid "Raft contact Z distance" msgstr "Расстояние от подложки до модели по вертикали" msgid "Z gap between object and raft. Ignored for soluble interface" -msgstr "" -"Зазор между моделью и подложкой. Значение игнорируется при выборе " -"растворимого материала." +msgstr "Зазор между моделью и подложкой. Значение игнорируется при выборе растворимого материала." msgid "Raft expansion" msgstr "Расширение подложки" @@ -9859,48 +7842,28 @@ msgid "Initial layer expansion" msgstr "Расширение первого слоя" msgid "Expand the first raft or support layer to improve bed plate adhesion" -msgstr "" -"Расширение первого слоя подложки или поддержки в плоскости XY для улучшения " -"адгезии с материалами склонными к отлипанию и закручиванию." +msgstr "Расширение первого слоя подложки или поддержки в плоскости XY для улучшения адгезии с материалами склонными к отлипанию и закручиванию." msgid "Raft layers" msgstr "Слоёв в подложке" -msgid "" -"Object will be raised by this number of support layers. Use this function to " -"avoid wrapping when print ABS" -msgstr "" -"Параметр устанавливает высоту подложки в слоях, тем самым поднимая модель на " -"заданное количество слоёв от стола. Используйте эту функцию, чтобы избежать " -"деформации при печати ABS пластиком." +msgid "Object will be raised by this number of support layers. Use this function to avoid wrapping when print ABS" +msgstr "Параметр устанавливает высоту подложки в слоях, тем самым поднимая модель на заданное количество слоёв от стола. Используйте эту функцию, чтобы избежать деформации при печати ABS пластиком." -msgid "" -"G-code path is genereated after simplifing the contour of model to avoid too " -"much points and gcode lines in gcode file. Smaller value means higher " -"resolution and more time to slice" -msgstr "" -"Разрешение G-кода. Путь G-кода создаётся после упрощения контура модели, " -"чтобы избежать слишком большого количества точек и линий в G-коде. Меньшее " -"значение означает более высокое разрешение и больше времени для нарезки." +msgid "G-code path is genereated after simplifing the contour of model to avoid too much points and gcode lines in gcode file. Smaller value means higher resolution and more time to slice" +msgstr "Разрешение G-кода. Путь G-кода создаётся после упрощения контура модели, чтобы избежать слишком большого количества точек и линий в G-коде. Меньшее значение означает более высокое разрешение и больше времени для нарезки." msgid "Travel distance threshold" msgstr "Порог перемещения для отката" -msgid "" -"Only trigger retraction when the travel distance is longer than this " -"threshold" -msgstr "" -"Откат будет срабатывать только в том случае, если расстояние перемещения " -"превысит этот порог." +msgid "Only trigger retraction when the travel distance is longer than this threshold" +msgstr "Откат будет срабатывать только в том случае, если расстояние перемещения превысит этот порог." msgid "Retract amount before wipe" msgstr "Величина отката перед очисткой" -msgid "" -"The length of fast retraction before wipe, relative to retraction length" -msgstr "" -"Длина быстрого отката перед очисткой, выраженная в процентах от общей длины " -"отката." +msgid "The length of fast retraction before wipe, relative to retraction length" +msgstr "Длина быстрого отката перед очисткой, выраженная в процентах от общей длины отката." msgid "Retract when change layer" msgstr "Откат при смене слоя" @@ -9914,25 +7877,14 @@ msgstr "Длина" msgid "Retraction Length" msgstr "Длина отката" -msgid "" -"Some amount of material in extruder is pulled back to avoid ooze during long " -"travel. Set zero to disable retraction" -msgstr "" -"Некоторое количество материала в экструдере откатывается назад, чтобы " -"избежать его течи при длительном перемещении. 0 - отключение отката." +msgid "Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction" +msgstr "Некоторое количество материала в экструдере откатывается назад, чтобы избежать его течи при длительном перемещении. 0 - отключение отката." msgid "Z hop when retract" msgstr "Подъём оси Z при откате" -msgid "" -"Whenever the retraction is done, the nozzle is lifted a little to create " -"clearance between nozzle and the print. It prevents nozzle from hitting the " -"print when travel move. Using spiral line to lift z can prevent stringing" -msgstr "" -"Здесь задаётся на сколько миллиметров будет каждый раз приподниматься ось Z, " -"когда срабатывает откат. Это предотвращает задевание соплом печатаемой " -"модели при перемещении. Использование спирального типа подъёма оси Z может " -"предотвратить образование паутины." +msgid "Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. It prevents nozzle from hitting the print when travel move. Using spiral line to lift z can prevent stringing" +msgstr "Здесь задаётся на сколько миллиметров будет каждый раз приподниматься ось Z, когда срабатывает откат. Это предотвращает задевание соплом печатаемой модели при перемещении. Использование спирального типа подъёма оси Z может предотвратить образование паутины." msgid "Z hop type" msgstr "Тип подъёма оси Z" @@ -9946,34 +7898,20 @@ msgstr "Спиральный" msgid "Only lift Z above" msgstr "Приподнимать ось Z только выше" -msgid "" -"If you set this to a positive value, Z lift will only take place above the " -"specified absolute Z." -msgstr "" -"Если указать положительное значение, ось Z будет подниматься только выше " -"(после) заданной здесь высоты (высота считается от стола). Таким образом вы " -"можете отключить подъём оси Z при печати первых слоёв." +msgid "If you set this to a positive value, Z lift will only take place above the specified absolute Z." +msgstr "Если указать положительное значение, ось Z будет подниматься только выше (после) заданной здесь высоты (высота считается от стола). Таким образом вы можете отключить подъём оси Z при печати первых слоёв." msgid "Only lift Z below" msgstr "Приподнимать ось Z только ниже" -msgid "" -"If you set this to a positive value, Z lift will only take place below the " -"specified absolute Z." -msgstr "" -"Если указать положительное значение, ось Z будет подниматься только ниже " -"(до) заданной здесь высоты (высота считается от стола). Таким образом вы " -"можете запретить подъём оси Z выше установленной высоты." +msgid "If you set this to a positive value, Z lift will only take place below the specified absolute Z." +msgstr "Если указать положительное значение, ось Z будет подниматься только ниже (до) заданной здесь высоты (высота считается от стола). Таким образом вы можете запретить подъём оси Z выше установленной высоты." msgid "On surfaces" msgstr "На поверхностях" -msgid "" -"Enforce Z Hop behavior. This setting is impacted by the above settings (Only " -"lift Z above/below)." -msgstr "" -"Принудительное поднятие оси Z. На этот параметр влияют указанные выше " -"параметры (Приподнимать ось Z только выше/ниже)." +msgid "Enforce Z Hop behavior. This setting is impacted by the above settings (Only lift Z above/below)." +msgstr "Принудительное поднятие оси Z. На этот параметр влияют указанные выше параметры (Приподнимать ось Z только выше/ниже)." msgid "All Surfaces" msgstr "Все верхние поверхности" @@ -9990,21 +7928,11 @@ msgstr "На верней и нижней" msgid "Extra length on restart" msgstr "Доп. длина подачи перед возобновлением печати" -msgid "" -"When the retraction is compensated after the travel move, the extruder will " -"push this additional amount of filament. This setting is rarely needed." -msgstr "" -"Дополнительная длина материала, которая будет выдавливаться после работы " -"отката и перемещения. Для увеличения длины выдавливания ставится " -"положительное значение (например 0.5 мм), для уменьшения отрицательное. Этот " -"параметр редко нуждается в правке." +msgid "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed." +msgstr "Дополнительная длина материала, которая будет выдавливаться после работы отката и перемещения. Для увеличения длины выдавливания ставится положительное значение (например 0.5 мм), для уменьшения отрицательное. Этот параметр редко нуждается в правке." -msgid "" -"When the retraction is compensated after changing tool, the extruder will " -"push this additional amount of filament." -msgstr "" -"Компенсация длины выдавливаемого пластика перед возобновлением печати после " -"смены сопла." +msgid "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament." +msgstr "Компенсация длины выдавливаемого пластика перед возобновлением печати после смены сопла." msgid "Retraction Speed" msgstr "Скорость извлечения при откате" @@ -10015,22 +7943,14 @@ msgstr "Скорость извлечения материала при отка msgid "Deretraction Speed" msgstr "Скорость заправки при откате" -msgid "" -"Speed for reloading filament into extruder. Zero means same speed with " -"retraction" -msgstr "" -"Скорость возврата материала при откате. Если оставить 0, будет " -"использоваться та же скорость что и при извлечении." +msgid "Speed for reloading filament into extruder. Zero means same speed with retraction" +msgstr "Скорость возврата материала при откате. Если оставить 0, будет использоваться та же скорость что и при извлечении." msgid "Use firmware retraction" msgstr "Использовать откат из прошивки" -msgid "" -"This experimental setting uses G10 and G11 commands to have the firmware " -"handle the retraction. This is only supported in recent Marlin." -msgstr "" -"Эта экспериментальная опция использует команды G10 и G11, чтобы прошивка " -"обрабатывала откаты. Поддерживается только в последних версиях Marlin." +msgid "This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin." +msgstr "Эта экспериментальная опция использует команды G10 и G11, чтобы прошивка обрабатывала откаты. Поддерживается только в последних версиях Marlin." msgid "Show auto-calibration marks" msgstr "Отображать на столе линии автокалибровки" @@ -10056,61 +7976,36 @@ msgstr "Случайно" msgid "Staggered inner seams" msgstr "Смещение внутренних швов" -msgid "" -"This option causes the inner seams to be shifted backwards based on their " -"depth, forming a zigzag pattern." -msgstr "" -"Этот параметр заставляет внутренние швы смещаться назад в зависимости от их " -"глубины, образуя зигзагообразный рисунок." +msgid "This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern." +msgstr "Этот параметр заставляет внутренние швы смещаться назад в зависимости от их глубины, образуя зигзагообразный рисунок." msgid "Seam gap" msgstr "Зазор шва" msgid "" -"In order to reduce the visibility of the seam in a closed loop extrusion, " -"the loop is interrupted and shortened by a specified amount.\n" -"This amount can be specified in millimeters or as a percentage of the " -"current extruder diameter. The default value for this parameter is 10%." +"In order to reduce the visibility of the seam in a closed loop extrusion, the loop is interrupted and shortened by a specified amount.\n" +"This amount can be specified in millimeters or as a percentage of the current extruder diameter. The default value for this parameter is 10%." msgstr "" -"Чтобы уменьшить видимость шва при печати замкнутого контура, контур будет " -"укорачиваться на заданную величину.\n" -"Это величина может быть указана в миллиметрах или в процентах от текущего " -"диаметра сопла. Значение по умолчанию - 10%." +"Чтобы уменьшить видимость шва при печати замкнутого контура, контур будет укорачиваться на заданную величину.\n" +"Это величина может быть указана в миллиметрах или в процентах от текущего диаметра сопла. Значение по умолчанию - 10%." msgid "Role base wipe speed" msgstr "Скорость очистки по типу экструзии" -msgid "" -"The wipe speed is determined by the speed of the current extrusion role.e.g. " -"if a wipe action is executed immediately following an outer wall extrusion, " -"the speed of the outer wall extrusion will be utilized for the wipe action." -msgstr "" -"Скорость очистки будет определяться скоростью текущего типа экструзии, т.е " -"если операция очистки выполняется сразу после экструзии внешнего периметра, " -"то для очистки используется скорость экструзии внешнего периметра." +msgid "The wipe speed is determined by the speed of the current extrusion role.e.g. if a wipe action is executed immediately following an outer wall extrusion, the speed of the outer wall extrusion will be utilized for the wipe action." +msgstr "Скорость очистки будет определяться скоростью текущего типа экструзии, т.е если операция очистки выполняется сразу после экструзии внешнего периметра, то для очистки используется скорость экструзии внешнего периметра." msgid "Wipe on loops" msgstr "Очистка в периметры" -msgid "" -"To minimize the visibility of the seam in a closed loop extrusion, a small " -"inward movement is executed before the extruder leaves the loop." -msgstr "" -"Чтобы минимизировать видимость шва при экструзии по замкнутому контуру, " -"перед выходом экструдера из контура выполняется небольшое движение внутрь." +msgid "To minimize the visibility of the seam in a closed loop extrusion, a small inward movement is executed before the extruder leaves the loop." +msgstr "Чтобы минимизировать видимость шва при экструзии по замкнутому контуру, перед выходом экструдера из контура выполняется небольшое движение внутрь." msgid "Wipe speed" msgstr "Скорость очистки" -msgid "" -"The wipe speed is determined by the speed setting specified in this " -"configuration.If the value is expressed as a percentage (e.g. 80%), it will " -"be calculated based on the travel speed setting above.The default value for " -"this parameter is 80%" -msgstr "" -"Скорость очистки определяется текущей настройкой. Если задано в процентах, " -"то она вычисляться относительно скорости перемещения. 80% - значение по " -"умолчанию." +msgid "The wipe speed is determined by the speed setting specified in this configuration.If the value is expressed as a percentage (e.g. 80%), it will be calculated based on the travel speed setting above.The default value for this parameter is 80%" +msgstr "Скорость очистки определяется текущей настройкой. Если задано в процентах, то она вычисляться относительно скорости перемещения. 80% - значение по умолчанию." msgid "Skirt distance" msgstr "Расстояние до юбки" @@ -10136,66 +8031,33 @@ msgstr "Скорость печати юбки" msgid "Speed of skirt, in mm/s. Zero means use default layer extrusion speed." msgstr "Скорость печати юбки (мм/с). 0 - скорость экструзии слоя по умолчанию." -msgid "" -"The printing speed in exported gcode will be slowed down, when the estimated " -"layer time is shorter than this value, to get better cooling for these layers" -msgstr "" -"Скорость печати в экспортированном G-коде будет замедлена, если расчётное " -"время печати слоя меньше этого значения, для обеспечения лучшего охлаждения " -"этих слоёв." +msgid "The printing speed in exported gcode will be slowed down, when the estimated layer time is shorter than this value, to get better cooling for these layers" +msgstr "Скорость печати в экспортированном G-коде будет замедлена, если расчётное время печати слоя меньше этого значения, для обеспечения лучшего охлаждения этих слоёв." msgid "Minimum sparse infill threshold" msgstr "Мин. порог разреженного заполнения" -msgid "" -"Sparse infill area which is smaller than threshold value is replaced by " -"internal solid infill" -msgstr "" -"Область с разреженным заполнением, размер которого меньше этого порогового " -"значения, заменяется сплошным заполнением." +msgid "Sparse infill area which is smaller than threshold value is replaced by internal solid infill" +msgstr "Область с разреженным заполнением, размер которого меньше этого порогового значения, заменяется сплошным заполнением." -msgid "" -"Line width of internal solid infill. If expressed as a %, it will be " -"computed over the nozzle diameter." -msgstr "" -"Ширина экструзии для внутреннего сплошного заполнения. Если задано в " -"процентах, то значение вычисляться относительно диаметра сопла." +msgid "Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для внутреннего сплошного заполнения. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Speed of internal solid infill, not the top and bottom surface" -msgstr "" -"Скорость печати внутреннего сплошного заполнения, за исключением верхней и " -"нижней поверхностей." +msgstr "Скорость печати внутреннего сплошного заполнения, за исключением верхней и нижней поверхностей." msgid "Spiral vase" msgstr "Спиральная ваза" -msgid "" -"Spiralize smooths out the z moves of the outer contour. And turns a solid " -"model into a single walled print with solid bottom layers. The final " -"generated model has no seam" -msgstr "" -"Печать спиральных и пустотелых, а также тонкостенных моделей. Модель " -"печатается в одну стенку без верней поверхности, заполнения и поддержки. При " -"этом сопло движется вдоль периметра непрерывно постепенно поднимаясь, так " -"получаются ровные красивые вазы без видимых швов." +msgid "Spiralize smooths out the z moves of the outer contour. And turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam" +msgstr "Печать спиральных и пустотелых, а также тонкостенных моделей. Модель печатается в одну стенку без верней поверхности, заполнения и поддержки. При этом сопло движется вдоль периметра непрерывно постепенно поднимаясь, так получаются ровные красивые вазы без видимых швов." msgid "" -"If smooth or traditional mode is selected, a timelapse video will be " -"generated for each print. After each layer is printed, a snapshot is taken " -"with the chamber camera. All of these snapshots are composed into a " -"timelapse video when printing completes. If smooth mode is selected, the " -"toolhead will move to the excess chute after each layer is printed and then " -"take a snapshot. Since the melt filament may leak from the nozzle during the " -"process of taking a snapshot, prime tower is required for smooth mode to " -"wipe nozzle." +"If smooth or traditional mode is selected, a timelapse video will be generated for each print. After each layer is printed, a snapshot is taken with the chamber camera. All of these snapshots are composed into a timelapse video when printing completes. If smooth mode is selected, the toolhead will move to the excess chute after each layer is printed and then take a snapshot. Since the melt " +"filament may leak from the nozzle during the process of taking a snapshot, prime tower is required for smooth mode to wipe nozzle." msgstr "" -"Если выбран плавный или обычный режим записи, то при каждой печати будет " -"создаваться ускоренное видео печати. После печати каждого слоя встроенная " -"камера делает снимок и по её завершении все эти снимки объединяются в единое " -"ускоренное видео. Если включён плавный режим, то после печати каждого слоя " -"головка перемещается к лотку для удаления излишков, а уже затем делается " -"снимок. Очистка сопла на черновой башне обязательна, т.к. при плавном режиме " -"возможно вытекание материалы из сопла когда делается снимок." +"Если выбран плавный или обычный режим записи, то при каждой печати будет создаваться ускоренное видео печати. После печати каждого слоя встроенная камера делает снимок и по её завершении все эти снимки объединяются в единое ускоренное видео. Если включён плавный режим, то после печати каждого слоя головка перемещается к лотку для удаления излишков, а уже затем делается снимок. Очистка сопла на " +"черновой башне обязательна, т.к. при плавном режиме возможно вытекание материалы из сопла когда делается снимок." msgid "Traditional" msgstr "Обычный" @@ -10210,90 +8072,19 @@ msgid "Start G-code when start the whole printing" msgstr "G-код выполняемый при каждом запуске печати." msgid "Start G-code when start the printing of this filament" -msgstr "" -"Стартовый G-код выполняемый при запуске печати с текущей пластиковой нитью." - -msgid "Single Extruder Multi Material" -msgstr "Мультиматериальный одиночный экструдер" - -msgid "Use single nozzle to print multi filament" -msgstr "" -"Использование одной экструзионной головы для печати несколькими видами/" -"цветами пластика." - -msgid "Manual Filament Change" -msgstr "Ручная замена прутка" - -msgid "" -"Enable this option to omit the custom Change filament G-code only at the " -"beginning of the print. The tool change command (e.g., T0) will be skipped " -"throughout the entire print. This is useful for manual multi-material " -"printing, where we use M600/PAUSE to trigger the manual filament change " -"action." -msgstr "" -"Включите эту опцию, если хотите пропустить пользовательский G-код смены " -"прутка только в начале печати. Команда смены инструмента (например, T0) " -"будет пропускаться на протяжении всей печати. Это полезно при ручной " -"мультиматериальной печати, где для запуска операции ручной смены прутка " -"используется команда M600." - -msgid "Purge in prime tower" -msgstr "Очистка в черновую башню" - -msgid "Purge remaining filament into prime tower" -msgstr "Очистка сопла от остатков материала в черновую башню" - -msgid "Enable filament ramming" -msgstr "Включить рэмминг прутка" - -msgid "No sparse layers (EXPERIMENTAL)" -msgstr "Отсутствие разреженных слоёв (экспериментально)" - -msgid "" -"If enabled, the wipe tower will not be printed on layers with no " -"toolchanges. On layers with a toolchange, extruder will travel downward to " -"print the wipe tower. User is responsible for ensuring there is no collision " -"with the print." -msgstr "" -"Если этот параметр включён, черновая башня не будет печататься на слоях где " -"не происходит смена инструмента. На слоях, где происходит смена инструмента, " -"экструдер будет опускаться вниз до верхней части черновой башни, чтобы " -"напечатать её. Эта функция помечена как экспериментальная, поэтому " -"пользователь несёт ответственность за то, чтобы не допустить столкновения " -"экструдера с напечатанным." - -msgid "Prime all printing extruders" -msgstr "Подготовка всех печатающих экструдеров" - -msgid "" -"If enabled, all printing extruders will be primed at the front edge of the " -"print bed at the start of the print." -msgstr "" -"Если этот параметр включён, все печатающие экструдеры в начале печати будут " -"подготавливаться на переднем крае стола." +msgstr "Стартовый G-код выполняемый при запуске печати с текущей пластиковой нитью." msgid "Slice gap closing radius" msgstr "Радиус закрытия пробелов при нарезке" -msgid "" -"Cracks smaller than 2x gap closing radius are being filled during the " -"triangle mesh slicing. The gap closing operation may reduce the final print " -"resolution, therefore it is advisable to keep the value reasonably low." -msgstr "" -"Трещины, меньше чем 2-кратный радиус закрытия пробелов, будут заполняться во " -"время нарезки треугольной сетки. Операция закрытия пробелов может привести к " -"снижению конечного разрешение печати, поэтому рекомендуется выставлять это " -"значение достаточно низким." +msgid "Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low." +msgstr "Трещины, меньше чем 2-кратный радиус закрытия пробелов, будут заполняться во время нарезки треугольной сетки. Операция закрытия пробелов может привести к снижению конечного разрешение печати, поэтому рекомендуется выставлять это значение достаточно низким." msgid "Slicing Mode" msgstr "Режим нарезки" -msgid "" -"Use \"Even-odd\" for 3DLabPrint airplane models. Use \"Close holes\" to " -"close all holes in the model." -msgstr "" -"Режим нарезки «чётный-нечётный» используется для моделей самолетов с ресурса " -"3DLabPrint. А «Закрытие отверстий» для закрытия всех отверстий в модели." +msgid "Use \"Even-odd\" for 3DLabPrint airplane models. Use \"Close holes\" to close all holes in the model." +msgstr "Режим нарезки «чётный-нечётный» используется для моделей самолетов с ресурса 3DLabPrint. А «Закрытие отверстий» для закрытия всех отверстий в модели." msgid "Regular" msgstr "Обычный" @@ -10304,34 +8095,14 @@ msgstr "Чётный-нечётный" msgid "Close holes" msgstr "Закрытие отверстий" -msgid "Z offset" -msgstr "Смещение координат оси Z" - -msgid "" -"This value will be added (or subtracted) from all the Z coordinates in the " -"output G-code. It is used to compensate for bad Z endstop position: for " -"example, if your endstop zero actually leaves the nozzle 0.3mm far from the " -"print bed, set this to -0.3 (or fix your endstop)." -msgstr "" -"Это значение будет прибавлено (или вычтено) из всех Z координат в выходном G-" -"коде. Это, например, используется для компенсации неправильного положения " -"концевика оси Z." - msgid "Enable support" msgstr "Включить поддержку" msgid "Enable support generation." msgstr "Включить генерацию поддержки." -msgid "" -"normal(auto) and tree(auto) is used to generate support automatically. If " -"normal(manual) or tree(manual) is selected, only support enforcers are " -"generated" -msgstr "" -"Тип поддержки «Обычная (авто)» и «Древовидная (авто)» используются для " -"автоматического создания поддержки. Если выбран тип поддержки «Обычная " -"(вручную)» или «Древовидная (вручную)», генерируется только принудительная " -"поддержка." +msgid "normal(auto) and tree(auto) is used to generate support automatically. If normal(manual) or tree(manual) is selected, only support enforcers are generated" +msgstr "Тип поддержки «Обычная (авто)» и «Древовидная (авто)» используются для автоматического создания поддержки. Если выбран тип поддержки «Обычная (вручную)» или «Древовидная (вручную)», генерируется только принудительная поддержка." msgid "normal(auto)" msgstr "Обычная (авто)" @@ -10355,9 +8126,7 @@ msgid "Pattern angle" msgstr "Угол печати шаблона поддержки" msgid "Use this setting to rotate the support pattern on the horizontal plane." -msgstr "" -"Используйте эту настройку для поворота шаблона поддержки в горизонтальной " -"плоскости." +msgstr "Используйте эту настройку для поворота шаблона поддержки в горизонтальной плоскости." msgid "On build plate only" msgstr "Поддержка только от стола" @@ -10368,73 +8137,47 @@ msgstr "Создавать поддержку только от стола." msgid "Support critical regions only" msgstr "Поддержка только критических областей" -msgid "" -"Only create support for critical regions including sharp tail, cantilever, " -"etc." -msgstr "" -"Создание поддержек только для критических областей, включая острые концы, " -"консоли (горизонтально выступающие элементы) и т.д." +msgid "Only create support for critical regions including sharp tail, cantilever, etc." +msgstr "Создание поддержек только для критических областей, включая острые концы, консоли (горизонтально выступающие элементы) и т.д." msgid "Remove small overhangs" msgstr "Игнорировать небольшие нависания" msgid "Remove small overhangs that possibly need no supports." -msgstr "" -"Не печатать поддержку под небольшими нависаниями, которые, как вам казалось, " -"нуждаются в них." +msgstr "Не печатать поддержку под небольшими нависаниями, которые, как вам казалось, нуждаются в них." msgid "Top Z distance" msgstr "Зазор поддержки сверху" msgid "The z gap between the top support interface and object" -msgstr "" -"Вертикальное расстояние между верхней частью модели и связующим слоем " -"поддержки." +msgstr "Вертикальное расстояние между верхней частью модели и связующим слоем поддержки." msgid "Bottom Z distance" msgstr "Зазор поддержки снизу" msgid "The z gap between the bottom support interface and object" -msgstr "" -"Вертикальное расстояние между нижней частью модели и связующим слоем " -"поддержки." +msgstr "Вертикальное расстояние между нижней частью модели и связующим слоем поддержки." msgid "Support/raft base" msgstr "Базовая поддержка/подложка" -msgid "" -"Filament to print support base and raft. \"Default\" means no specific " -"filament for support and current filament is used" -msgstr "" -"Пластиковая нить для печати базовой поддержки и плота. Значение «По " -"умолчанию» означает, что для поддержки используется текущая пластиковая нить." +msgid "Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used" +msgstr "Пластиковая нить для печати базовой поддержки и подложки. Значение «По умолчанию» означает, что для поддержки используется текущая пластиковая нить." -msgid "" -"Line width of support. If expressed as a %, it will be computed over the " -"nozzle diameter." -msgstr "" -"Ширина экструзии для поддержки. Если задано в процентах, то значение " -"вычисляться относительно диаметра сопла." +msgid "Line width of support. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для поддержки. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Interface use loop pattern" msgstr "Связующий слой петлями" -msgid "" -"Cover the top contact layer of the supports with loops. Disabled by default." -msgstr "" -"Печатать контактный слой связующего слоя поддержки петлями. По умолчанию " -"отключено." +msgid "Cover the top contact layer of the supports with loops. Disabled by default." +msgstr "Печатать контактный слой связующего слоя поддержки петлями. По умолчанию отключено." msgid "Support/raft interface" msgstr "Связующий слой поддержки/подложки" -msgid "" -"Filament to print support interface. \"Default\" means no specific filament " -"for support interface and current filament is used" -msgstr "" -"Пластиковая нить для печати связующего слоя поддержки. Значение «По " -"умолчанию» означает, что для связующего слоя поддержки используется текущая " -"пластиковая нить." +msgid "Filament to print support interface. \"Default\" means no specific filament for support interface and current filament is used" +msgstr "Пластиковая нить для печати связующего слоя поддержки. Значение «По умолчанию» означает, что для связующего слоя поддержки используется текущая пластиковая нить." msgid "Top interface layers" msgstr "Связующих слоёв сверху" @@ -10449,17 +8192,13 @@ msgid "Top interface spacing" msgstr "Расстояние между линиями связующего слоя сверху" msgid "Spacing of interface lines. Zero means solid interface" -msgstr "" -"Расстояние между линиями связующего слоя сверху. Установите 0, чтобы " -"получить сплошной слой." +msgstr "Расстояние между линиями связующего слоя сверху. Установите 0, чтобы получить сплошной слой." msgid "Bottom interface spacing" msgstr "Расстояние между линиями связующего слоя снизу" msgid "Spacing of bottom interface lines. Zero means solid interface" -msgstr "" -"Расстояние между линиями связующего слоя снизу. Установите 0, чтобы получить " -"сплошной слой." +msgstr "Расстояние между линиями связующего слоя снизу. Установите 0, чтобы получить сплошной слой." msgid "Speed of support interface" msgstr "Скорость печати связующего слоя поддержки." @@ -10479,14 +8218,8 @@ msgstr "Полость" msgid "Interface pattern" msgstr "Шаблон связующего слоя" -msgid "" -"Line pattern of support interface. Default pattern for non-soluble support " -"interface is Rectilinear, while default pattern for soluble support " -"interface is Concentric" -msgstr "" -"Шаблон, по которому будет происходить печать связующего слоя поддержки. При " -"выборе по умолчанию, шаблон для нерастворимой связующей поддержки - " -"прямолинейный, для растворимой - концентрический." +msgid "Line pattern of support interface. Default pattern for non-soluble support interface is Rectilinear, while default pattern for soluble support interface is Concentric" +msgstr "Шаблон, по которому будет происходить печать связующего слоя поддержки. При выборе по умолчанию, шаблон для нерастворимой связующей поддержки - прямолинейный, для растворимой - концентрический." msgid "Rectilinear Interlaced" msgstr "Прямолинейный (чередование направлений)" @@ -10501,31 +8234,20 @@ msgid "Normal Support expansion" msgstr "Горизонтальное расширение поддержки" msgid "Expand (+) or shrink (-) the horizontal span of normal support" -msgstr "" -"Горизонтальное расширение (+) или сужение (-) базовой поддержки в плоскости " -"XY." +msgstr "Горизонтальное расширение (+) или сужение (-) базовой поддержки в плоскости XY." msgid "Speed of support" msgstr "Скорость печати поддержки." msgid "" -"Style and shape of the support. For normal support, projecting the supports " -"into a regular grid will create more stable supports (default), while snug " -"support towers will save material and reduce object scarring.\n" -"For tree support, slim and organic style will merge branches more " -"aggressively and save a lot of material (default organic), while hybrid " -"style will create similar structure to normal support under large flat " -"overhangs." +"Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.\n" +"For tree support, slim style will merge branches more aggressively and save a lot of material (default), while hybrid style will create similar structure to normal support under large flat overhangs." msgstr "" "Стиль и форма создаваемой поддержки.\n" "\n" -"Стиль «Сетка» создаёт более устойчивые опоры (по умолчанию). Стиль " -"«Аккуратный» экономит материал и уменьшает образование дефектов на моделях.\n" +"Стиль «Сетка» создаёт более устойчивые опоры. Стиль «Аккуратный» экономит материал и уменьшает образование дефектов на моделях.\n" "\n" -"Для древовидной поддержки, при стройном и органическом стиле происходит " -"более агрессивное объединение ветвей и экономия материала (по умолчанию " -"органический). В то время как гибридный стиль создаёт структуру, схожую с " -"обычную поддержкой при больших плоских нависаниях." +"Для древовидной поддержки, при стройном стиле происходит более агрессивное объединение ветвей и экономия материала (по умолчанию). В то время как гибридный стиль создаёт структуру, схожую с обычную поддержкой при больших плоских нависаниях." msgid "Snug" msgstr "Аккуратный" @@ -10545,94 +8267,45 @@ msgstr "Органический" msgid "Independent support layer height" msgstr "Независимая высота слоя поддержки" -msgid "" -"Support layer uses layer height independent with object layer. This is to " -"support customizing z-gap and save print time.This option will be invalid " -"when the prime tower is enabled." -msgstr "" -"Слои поддержки будут иметь высоту слоя, отличную от высоты слоя модели. Это " -"необходимо для настройки зазора между моделью и поддержкой для экономии " -"времени печати. Опция неактивна, когда включена черновая башня." +msgid "Support layer uses layer height independent with object layer. This is to support customizing z-gap and save print time.This option will be invalid when the prime tower is enabled." +msgstr "Слои поддержки будут иметь высоту слоя, отличную от высоты слоя модели. Это необходимо для настройки зазора между моделью и поддержкой для экономии времени печати. Опция неактивна, когда включена черновая башня." msgid "Threshold angle" msgstr "Пороговый угол поддержки" -msgid "" -"Support will be generated for overhangs whose slope angle is below the " -"threshold." -msgstr "" -"Для нависаний, угол наклона которых ниже заданного порогового значения, " -"будут использоваться поддержки." +msgid "Support will be generated for overhangs whose slope angle is below the threshold." +msgstr "Для нависаний, угол наклона которых ниже заданного порогового значения, будут использоваться поддержки." msgid "Tree support branch angle" msgstr "Угол нависания ветвей древовидной поддержки" -msgid "" -"This setting determines the maximum overhang angle that t he branches of " -"tree support allowed to make.If the angle is increased, the branches can be " -"printed more horizontally, allowing them to reach farther." -msgstr "" -"Этот параметр определяет максимальный угол нависания ветвей древовидной " -"поддержки. При увеличении угла, ветви печатаются более горизонтально, что " -"позволяет им достигать большего охвата. При указании меньшего угла, " -"поддержка будет более вертикальной и устойчивой." +msgid "This setting determines the maximum overhang angle that t he branches of tree support allowed to make.If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther." +msgstr "Этот параметр определяет максимальный угол нависания ветвей древовидной поддержки. При увеличении угла, ветви печатаются более горизонтально, что позволяет им достигать большего охвата. При указании меньшего угла, поддержка будет более вертикальной и устойчивой." msgid "Preferred Branch Angle" msgstr "Предпочтительный угол ответвления" #. TRN PrintSettings: "Organic supports" > "Preferred Branch Angle" -msgid "" -"The preferred angle of the branches, when they do not have to avoid the " -"model. Use a lower angle to make them more vertical and more stable. Use a " -"higher angle for branches to merge faster." -msgstr "" -"Предпочтительный угол ответвления ветвей, при котором не нужно избегать " -"модель. При указании меньшего угла поддержка будет более вертикальной и " -"устойчивой. Для получения большего охвата указывайте более высокий угол." +msgid "The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster." +msgstr "Предпочтительный угол ответвления ветвей, при котором не нужно избегать модель. При указании меньшего угла поддержка будет более вертикальной и устойчивой. Для получения большего охвата указывайте более высокий угол." msgid "Tree support branch distance" msgstr "Расстояние между ветвями древовидной поддержки" -msgid "" -"This setting determines the distance between neighboring tree support nodes." -msgstr "" -"Этот параметр определяет, насколько далеко должны друг от друга " -"располагаться ветви при касании модели." - -msgid "Branch Density" -msgstr "Плотность ветвей" - -#. TRN PrintSettings: "Organic supports" > "Branch Density" -msgid "" -"Adjusts the density of the support structure used to generate the tips of " -"the branches. A higher value results in better overhangs but the supports " -"are harder to remove, thus it is recommended to enable top support " -"interfaces instead of a high branch density value if dense interfaces are " -"needed." -msgstr "" -"Регулирует плотность создания ветвей в месте контакта с моделью. Большее " -"значение приводит к улучшению качества печати нависаний, но такие поддержки " -"сложнее удалять, поэтому рекомендуется вместо высокого значения плотности " -"ветвей включать связующие слои поддержки." +msgid "This setting determines the distance between neighboring tree support nodes." +msgstr "Этот параметр определяет, насколько далеко должны друг от друга располагаться ветви при касании модели." msgid "Adaptive layer height" msgstr "Переменная высота слоёв" -msgid "" -"Enabling this option means the height of tree support layer except the " -"first will be automatically calculated " -msgstr "" -"Включение автоматического расчёта высоты слоя древовидной поддержки, кроме " -"первого слоя." +msgid "Enabling this option means the height of tree support layer except the first will be automatically calculated " +msgstr "Включение автоматического расчёта высоты слоя древовидной поддержки, кроме первого слоя." msgid "Auto brim width" msgstr "Автоширина каймы" -msgid "" -"Enabling this option means the width of the brim for tree support will be " -"automatically calculated" -msgstr "" -"Включение автоматического расчёта ширины каймы для древовидной поддержки." +msgid "Enabling this option means the width of the brim for tree support will be automatically calculated" +msgstr "Включение автоматического расчёта ширины каймы для древовидной поддержки." msgid "Tree support brim width" msgstr "Ширина каймы древовидной поддержки" @@ -10640,132 +8313,55 @@ msgstr "Ширина каймы древовидной поддержки" msgid "Distance from tree branch to the outermost brim line" msgstr "Расстояние от древовидной поддержки до внешней линии каймы." -msgid "Tip Diameter" -msgstr "Диаметр кончика ветки" - -#. TRN PrintSettings: "Organic supports" > "Tip Diameter" -msgid "Branch tip diameter for organic supports." -msgstr "Диаметр кончика ветки органической поддержки." - # ??? msgid "Tree support branch diameter" msgstr "Диаметр ветвей древовидной поддержки" msgid "This setting determines the initial diameter of support nodes." -msgstr "" -"Этот параметр определяет начальный диаметр ветвей, т.е. их диаметр в месте " -"контакта с моделью." - -#. TRN PrintSettings: #lmFIXME -msgid "Branch Diameter Angle" -msgstr "Угол изменения диаметра ветвей" - -#. TRN PrintSettings: "Organic supports" > "Branch Diameter Angle" -msgid "" -"The angle of the branches' diameter as they gradually become thicker towards " -"the bottom. An angle of 0 will cause the branches to have uniform thickness " -"over their length. A bit of an angle can increase stability of the organic " -"support." -msgstr "" -"Угол изменения диаметра ветвей по мере их постепенного утолщения к " -"основанию. Если значение угла равно 0, ветви будут иметь одинаковую толщину " -"по всей своей длине. Небольшой угол может повысить устойчивость органической " -"поддержки." - -msgid "Branch Diameter with double walls" -msgstr "Диаметр ветвей с двойными стенками" - -#. TRN PrintSettings: "Organic supports" > "Branch Diameter" -msgid "" -"Branches with area larger than the area of a circle of this diameter will be " -"printed with double walls for stability. Set this value to zero for no " -"double walls." -msgstr "" -"Ветви, толщина которых больше указанного диаметра, будут напечатаны с " -"двойными стенками для прочности. Установите 0, если двойные стенки у ветвей " -"не нужны." +msgstr "Этот параметр определяет начальный диаметр ветвей, т.е. их диаметр в месте контакта с моделью." msgid "Tree support wall loops" msgstr "Периметров древовидной поддержки" msgid "This setting specify the count of walls around tree support" -msgstr "" -"Этот параметр определяет количество периметров у печатаемой древовидной " -"поддержки." +msgstr "Этот параметр определяет количество периметров у печатаемой древовидной поддержки." msgid "Tree support with infill" msgstr "Древовидная поддержка с заполнением" -msgid "" -"This setting specifies whether to add infill inside large hollows of tree " -"support" -msgstr "" -"Этот параметр определяет, следует ли заполнять большие полости внутри " -"древовидной поддержки." - -msgid "Activate temperature control" -msgstr "Вкл. контроль температуры" - -msgid "" -"Enable this option for chamber temperature control. An M191 command will be " -"added before \"machine_start_gcode\"\n" -"G-code commands: M141/M191 S(0-255)" -msgstr "" -"Для контроля температуры в камере принтера включите эту опцию. Команда M191 " -"будет добавлена перед стартовый G-кодом принтера (machine_start_gcode).\n" -"G-код команда: M141/M191 S(0-255)" +msgid "This setting specifies whether to add infill inside large hollows of tree support" +msgstr "Этот параметр определяет, следует ли заполнять большие полости внутри древовидной поддержки." msgid "Chamber temperature" msgstr "Температура термокамеры" -msgid "" -"Higher chamber temperature can help suppress or reduce warping and " -"potentially lead to higher interlayer bonding strength for high temperature " -"materials like ABS, ASA, PC, PA and so on.At the same time, the air " -"filtration of ABS and ASA will get worse.While for PLA, PETG, TPU, PVA and " -"other low temperature materials,the actual chamber temperature should not be " -"high to avoid cloggings, so 0 which stands for turning off is highly " -"recommended" -msgstr "" -"Более высокая температура в камере может помочь уменьшить или даже исключить " -"коробление материала. Так же это улучшает межслойное соединения у " -"высокотемпературных материалов, таких как ABS, ASA, PC, PA и т.д. (в то же " -"время фильтрация воздуха при печати ABS и ASA сделает её хуже). Для " -"низкотемпературных материалов, таких как PLA, PETG, TPU, PVA и т. д., " -"фактическая температура в камере не должна быть слишком высокой, чтобы " -"избежать засорения сопла, поэтому настоятельно рекомендуется установить " -"температуру в камере равной 0°C." +msgid "Target chamber temperature" +msgstr "Температура, которую необходимо поддерживать внутри принтера." msgid "Nozzle temperature for layers after the initial one" msgstr "Температура сопла при печати для слоёв после первого." +msgid "Bed temperature difference" +msgstr "Разница температур подогреваемого стола" + +msgid "Do not recommend bed temperature of other layer to be lower than initial layer for more than this threshold. Too low bed temperature of other layer may cause the model broken free from build plate" +msgstr "Не рекомендуется, чтобы температура последующих слоёв была ниже температуры первого слоя, более чем на это пороговое значение. Слишком низкая температура последующих слоёв может привести к отрыву модели от стола." + msgid "Detect thin wall" msgstr "Обнаружение тонких стенок" -msgid "" -"Detect thin wall which can't contain two line width. And use single line to " -"print. Maybe printed not very well, because it's not closed loop" -msgstr "" -"Обнаружение тонких стенок (стенки одинарной ширины), которые можно " -"напечатать только в один проход экструдера. Возможно, будет напечатано не " -"очень хорошо, так как это не замкнутый контур." +msgid "Detect thin wall which can't contain two line width. And use single line to print. Maybe printed not very well, because it's not closed loop" +msgstr "Обнаружение тонких стенок (стенки одинарной ширины), которые можно напечатать только в один проход экструдера. Возможно, будет напечатано не очень хорошо, так как это не замкнутый контур." -msgid "" -"This gcode is inserted when change filament, including T command to trigger " -"tool change" -msgstr "" -"Этот G-код вставляется при смене материала, включая команду T для запуска " -"смены инструмента." +msgid "This gcode is inserted when change filament, including T command to trigger tool change" +msgstr "Этот G-код вставляется при смене материала, включая команду T для запуска смены инструмента." +# ??? msgid "This gcode is inserted when the extrusion role is changed" -msgstr "" +msgstr "Этот G-код вставляется при смене роли экструзии." -msgid "" -"Line width for top surfaces. If expressed as a %, it will be computed over " -"the nozzle diameter." -msgstr "" -"Ширина экструзии для верхней поверхности. Если задано в процентах, то " -"значение вычисляться относительно диаметра сопла." +msgid "Line width for top surfaces. If expressed as a %, it will be computed over the nozzle diameter." +msgstr "Ширина экструзии для верхней поверхности. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Speed of top surface infill which is solid" msgstr "Скорость печати верхних сплошных поверхностей." @@ -10773,14 +8369,8 @@ msgstr "Скорость печати верхних сплошных повер msgid "Top shell layers" msgstr "Сплошных слоёв сверху" -msgid "" -"This is the number of solid layers of top shell, including the top surface " -"layer. When the thickness calculated by this value is thinner than top shell " -"thickness, the top shell layers will be increased" -msgstr "" -"Количество сплошных слоёв при печати верхней поверхности модели. Если " -"толщина, рассчитанная с помощью этого значения, меньше толщины оболочки " -"сверху, количество сплошных слоёв сверху будет увеличено." +msgid "This is the number of solid layers of top shell, including the top surface layer. When the thickness calculated by this value is thinner than top shell thickness, the top shell layers will be increased" +msgstr "Количество сплошных слоёв при печати верхней поверхности модели. Если толщина, рассчитанная с помощью этого значения, меньше толщины оболочки сверху, количество сплошных слоёв сверху будет увеличено." msgid "Top solid layers" msgstr "Верхних сплошных слоёв" @@ -10788,20 +8378,10 @@ msgstr "Верхних сплошных слоёв" msgid "Top shell thickness" msgstr "Толщина оболочки сверху" -msgid "" -"The number of top solid layers is increased when slicing if the thickness " -"calculated by top shell layers is thinner than this value. This can avoid " -"having too thin shell when layer height is small. 0 means that this setting " -"is disabled and thickness of top shell is absolutely determained by top " -"shell layers" +msgid "The number of top solid layers is increased when slicing if the thickness calculated by top shell layers is thinner than this value. This can avoid having too thin shell when layer height is small. 0 means that this setting is disabled and thickness of top shell is absolutely determained by top shell layers" msgstr "" -"Минимальная толщина оболочки сверху в мм. Если толщина оболочки, " -"рассчитанная по количеству сплошных слоёв сверху, меньше этого значения, " -"количество сплошных слоёв сверху будет автоматически увеличено при нарезке, " -"для удовлетворения минимальной толщины оболочки. Это позволяет избежать " -"слишком тонкой оболочки при небольшой высоте слоя. 0 означает, что этот " -"параметр отключён, а толщина оболочки сверху полностью задаётся количеством " -"сплошных слоёв снизу." +"Минимальная толщина оболочки сверху в мм. Если толщина оболочки, рассчитанная по количеству сплошных слоёв сверху, меньше этого значения, количество сплошных слоёв сверху будет автоматически увеличено при нарезке, для удовлетворения минимальной толщины оболочки. Это позволяет избежать слишком тонкой оболочки при небольшой высоте слоя. 0 означает, что этот параметр отключён, а толщина оболочки " +"сверху полностью задаётся количеством сплошных слоёв снизу." msgid "Speed of travel which is faster and without extrusion" msgstr "Скорость перемещения экструдера при позиционировании без печати." @@ -10809,31 +8389,17 @@ msgstr "Скорость перемещения экструдера при по msgid "Wipe while retracting" msgstr "Очистка сопла при откате" -msgid "" -"Move nozzle along the last extrusion path when retracting to clean leaked " -"material on nozzle. This can minimize blob when print new part after travel" -msgstr "" -"Позволяет соплу совершать движение очистки во время отката, перемещая его " -"вдоль последнего пути экструзии. Это может снизить появление дефектов " -"(каплей, пупырышек) при печати новой детали после перемещения." +msgid "Move nozzle along the last extrusion path when retracting to clean leaked material on nozzle. This can minimize blob when print new part after travel" +msgstr "Позволяет соплу совершать движение очистки во время отката, перемещая его вдоль последнего пути экструзии. Это может снизить появление дефектов (каплей, пупырышек) при печати новой детали после перемещения." msgid "Wipe Distance" msgstr "Расстояние очистки внешней стенки" -msgid "" -"Discribe how long the nozzle will move along the last path when retracting" -msgstr "" -"Задаёт расстояние перемещения, добавленное после печати внешней стенки при " -"совершении отката, чтобы сделать шов по оси Z менее заметным." +msgid "Discribe how long the nozzle will move along the last path when retracting" +msgstr "Задаёт расстояние перемещения, добавленное после печати внешней стенки при совершении отката, чтобы сделать шов по оси Z менее заметным." -msgid "" -"The wiping tower can be used to clean up the residue on the nozzle and " -"stabilize the chamber pressure inside the nozzle, in order to avoid " -"appearance defects when printing objects." -msgstr "" -"Башня очистки используется для очистки сопла от остатков материала и " -"стабилизации давления внутри сопла, чтобы избежать дефектов снаружи " -"печатаемой модели." +msgid "The wiping tower can be used to clean up the residue on the nozzle and stabilize the chamber pressure inside the nozzle, in order to avoid appearance defects when printing objects." +msgstr "Башня очистки используется для очистки сопла от остатков материала и стабилизации давления внутри сопла, чтобы избежать дефектов снаружи печатаемой модели." msgid "Purging volumes" msgstr "Объём очистки" @@ -10841,19 +8407,14 @@ msgstr "Объём очистки" msgid "Flush multiplier" msgstr "Множитель очистки" -msgid "" -"The actual flushing volumes is equal to the flush multiplier multiplied by " -"the flushing volumes in the table." -msgstr "" -"Реальные объёмы очистки равны множителю очистки, умноженному на объёмы " -"очистки указанные в таблице." +msgid "The actual flushing volumes is equal to the flush multiplier multiplied by the flushing volumes in the table." +msgstr "Реальные объёмы очистки равны множителю очистки, умноженному на объёмы очистки указанные в таблице." msgid "Prime volume" msgstr "Объём сброса на черновой башни" msgid "The volume of material to prime extruder on tower." -msgstr "" -"Объём выдавленного материала для заправки экструдера на черновой башне." +msgstr "Объём выдавленного материала для заправки экструдера на черновой башне." msgid "Width" msgstr "Ширина" @@ -10870,13 +8431,8 @@ msgstr "Угол поворота черновой башни относител msgid "Stabilization cone apex angle" msgstr "Угол вершины стабилизирующего конуса" -msgid "" -"Angle at the apex of the cone that is used to stabilize the wipe tower. " -"Larger angle means wider base." -msgstr "" -"Регулировка угла «стабилизирующего конуса», который используется для " -"предотвращения опрокидывания черновой башни. Больший угол означает более " -"широкое основание конуса." +msgid "Angle at the apex of the cone that is used to stabilize the wipe tower. Larger angle means wider base." +msgstr "Регулировка угла «стабилизирующего конуса», который используется для предотвращения опрокидывания черновой башни. Больший угол означает более широкое основание конуса." msgid "Wipe tower purge lines spacing" msgstr "Расстояние между линиями очистки черновой башни" @@ -10887,177 +8443,60 @@ msgstr "Расстояние между линиями очистки на че msgid "Wipe tower extruder" msgstr "Экструдер черновой башни" -msgid "" -"The extruder to use when printing perimeter of the wipe tower. Set to 0 to " -"use the one that is available (non-soluble would be preferred)." -msgstr "" -"Номер экструдера, которым печатаются периметры черновой башни. Установите 0, " -"чтобы использовать тот, который доступен (предпочтительнее нерастворимый)." +msgid "The extruder to use when printing perimeter of the wipe tower. Set to 0 to use the one that is available (non-soluble would be preferred)." +msgstr "Номер экструдера, которым печатаются периметры черновой башни. Установите 0, чтобы использовать тот, который доступен (предпочтительнее нерастворимый)." msgid "Purging volumes - load/unload volumes" msgstr "Объём очистки - Объём загрузки/выгрузки" -msgid "" -"This vector saves required volumes to change from/to each tool used on the " -"wipe tower. These values are used to simplify creation of the full purging " -"volumes below." -msgstr "" -"Этот параметр задаёт объём материала, который будет выдавлен на черновую " -"башню для прочистки сопла при смене экструдеров/инструментов. Эти значения " -"используются для упрощения создания полноты объёмов очистки указанной ниже." +msgid "This vector saves required volumes to change from/to each tool used on the wipe tower. These values are used to simplify creation of the full purging volumes below." +msgstr "Этот параметр задаёт объём материала, который будет выдавлен на черновую башню для прочистки сопла при смене экструдеров/инструментов. Эти значения используются для упрощения создания полноты объёмов очистки указанной ниже." -msgid "" -"Purging after filament change will be done inside objects' infills. This may " -"lower the amount of waste and decrease the print time. If the walls are " -"printed with transparent filament, the mixed color infill will be seen " -"outside. It will not take effect, unless the prime tower is enabled." -msgstr "" -"Очистка сопла после смены материала будет производиться в заполнение модели. " -"Это снижает количество отходов и сокращает время печати. Эта функция " -"работает только при включенной черновой башне." +msgid "Purging after filament change will be done inside objects' infills. This may lower the amount of waste and decrease the print time. If the walls are printed with transparent filament, the mixed color infill will be seen outside. It will not take effect, unless the prime tower is enabled." +msgstr "Очистка сопла после смены материала будет производиться в заполнение модели. Это снижает количество отходов и сокращает время печати. Эта функция работает только при включенной черновой башне." -msgid "" -"Purging after filament change will be done inside objects' support. This may " -"lower the amount of waste and decrease the print time. It will not take " -"effect, unless the prime tower is enabled." -msgstr "" -"Очистка сопла после смены материала будет производиться в поддержку модели. " -"Это снижает количество отходов и сокращает время печати. Эта функция " -"работает только при включенной черновой башне." +msgid "Purging after filament change will be done inside objects' support. This may lower the amount of waste and decrease the print time. It will not take effect, unless the prime tower is enabled." +msgstr "Очистка сопла после смены материала будет производиться в поддержку модели. Это снижает количество отходов и сокращает время печати. Эта функция работает только при включенной черновой башне." -msgid "" -"This object will be used to purge the nozzle after a filament change to save " -"filament and decrease the print time. Colours of the objects will be mixed " -"as a result. It will not take effect, unless the prime tower is enabled." -msgstr "" -"Эта модель будет использоваться для очистки сопла после смены материала для " -"его экономии и сокращения времени печати. В результате цвета будут " -"смешиваться. Это не будет действовать, если не будет включена черновая башня." +msgid "This object will be used to purge the nozzle after a filament change to save filament and decrease the print time. Colours of the objects will be mixed as a result. It will not take effect, unless the prime tower is enabled." +msgstr "Эта модель будет использоваться для очистки сопла после смены материала для его экономии и сокращения времени печати. В результате цвета будут смешиваться. Это не будет действовать, если не будет включена черновая башня." msgid "Maximal bridging distance" msgstr "Максимальное длина моста" msgid "Maximal distance between supports on sparse infill sections." -msgstr "" -"Максимальное расстояние между опорами на разряженных участках заполнения." +msgstr "Максимальное расстояние между опорами на разряженных участках заполнения." msgid "X-Y hole compensation" msgstr "Коррекция размеров отверстий по XY" -msgid "" -"Holes of object will be grown or shrunk in XY plane by the configured value. " -"Positive value makes holes bigger. Negative value makes holes smaller. This " -"function is used to adjust size slightly when the object has assembling issue" -msgstr "" -"Отверстия модели будут увеличены или уменьшены в плоскости XY на заданное " -"значение. Положительное значение увеличивает отверстия, отрицательное - " -"уменьшает. Эта функция используется для небольшой корректировки размера, " -"когда возникают проблемы со сборкой." +msgid "Holes of object will be grown or shrunk in XY plane by the configured value. Positive value makes holes bigger. Negative value makes holes smaller. This function is used to adjust size slightly when the object has assembling issue" +msgstr "Отверстия модели будут увеличены или уменьшены в плоскости XY на заданное значение. Положительное значение увеличивает отверстия, отрицательное - уменьшает. Эта функция используется для небольшой корректировки размера, когда возникают проблемы со сборкой." msgid "X-Y contour compensation" msgstr "Коррекция размеров модели по XY" -msgid "" -"Contour of object will be grown or shrunk in XY plane by the configured " -"value. Positive value makes contour bigger. Negative value makes contour " -"smaller. This function is used to adjust size slightly when the object has " -"assembling issue" -msgstr "" -"Параметр отвечает за смещение границы контура печатаемой модели в плоскости " -"XY на заданное значение. Положительное значение увеличивает контур. " -"Отрицательное значение уменьшает контур. Эта функция используется для " -"небольшой корректировки размера, когда возникают проблемы со сборкой." - -# ??? -# Преобразование отверстий в многограннники -msgid "Convert holes to polyholes" -msgstr "Многогранные отверстия" - -# ??? -# Поиск отверстий близких к кругу в двух или более слоях -# Расчёт многогранного отверстия вычисляется по диаметру сопла. -msgid "" -"Search for almost-circular holes that span more than one layer and convert " -"the geometry to polyholes. Use the nozzle size and the (biggest) diameter to " -"compute the polyhole.\n" -"See http://hydraraptor.blogspot.com/2011/02/polyholes.html" -msgstr "" -"Поиск цилиндрических отверстий в двух или более слоях и преобразование их " -"геометрии в многогранники. Для расчёта многогранного отверстия используется " -"размер сопла и наибольший диаметр найденного отверстия.\n" -"Подробнее на http://hydraraptor.blogspot.com/2011/02/polyholes.html" - -# ??? -# Предел обнаружения многогранного отверстия -msgid "Polyhole detection margin" -msgstr "Предел обнаружения" - -#, fuzzy, c-format, boost-format -msgid "" -"Maximum defection of a point to the estimated radius of the circle.\n" -"As cylinders are often exported as triangles of varying size, points may not " -"be on the circle circumference. This setting allows you some leway to " -"broaden the detection.\n" -"In mm or in % of the radius." -msgstr "" -"Максимальное отклонение точки от расчётного радиуса окружности.\n" -"Поскольку цилиндры часто экспортируются в виде треугольников разного " -"размера, точки могут находиться не на окружности круга. Эта настройка " -"позволяет в некоторой степени расширить эту область обнаружения.\n" -"Значение задаётся в мм или в процентах от радиуса." - -msgid "Polyhole twist" -msgstr "Скручивание многогранника" - -msgid "Rotate the polyhole every layer." -msgstr "Вращение многогранного отверстия на каждом слое." +msgid "Contour of object will be grown or shrunk in XY plane by the configured value. Positive value makes contour bigger. Negative value makes contour smaller. This function is used to adjust size slightly when the object has assembling issue" +msgstr "Параметр отвечает за смещение границы контура печатаемой модели в плоскости XY на заданное значение. Положительное значение увеличивает контур. Отрицательное значение уменьшает контур. Эта функция используется для небольшой корректировки размера, когда возникают проблемы со сборкой." msgid "G-code thumbnails" msgstr "Эскизы G-кода" -msgid "" -"Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the " -"following format: \"XxY, XxY, ...\"" -msgstr "" -"Размеры изображения, которые будут сохранены в файлах .sl1 / .sl1s в " -"следующем формате: \"XxY, XxY, ...\"" - -msgid "Format of G-code thumbnails" -msgstr "Формат эскизов G-кода" - -msgid "" -"Format of G-code thumbnails: PNG for best quality, JPG for smallest size, " -"QOI for low memory firmware" -msgstr "" -"Формат эскизов G-кода: PNG для наилучшего качества, JPG для наименьшего " -"размера, QOI для прошивки с малым объемом памяти." +msgid "Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"" +msgstr "Размеры изображения, которые будут сохранены в файлах .sl1 / .sl1s в следующем формате: \"XxY, XxY, ...\"" msgid "Use relative E distances" msgstr "Исп. относительные координаты для экструдера (E)" -msgid "" -"Relative extrusion is recommended when using \"label_objects\" option.Some " -"extruders work better with this option unckecked (absolute extrusion mode). " -"Wipe tower is only compatible with relative mode. It is always enabled on " -"BambuLab printers. Default is checked" +msgid "Relative extrusion is recommended when using \"label_objects\" option.Some extruders work better with this option unckecked (absolute extrusion mode). Wipe tower is only compatible with relative mode. It is always enabled on BambuLab printers. Default is checked" msgstr "" -"Относительная экструзия рекомендуется при использовании опции «Название " -"моделей».\n" +"Относительная экструзия рекомендуется при использовании опции «Название моделей».\n" "\n" -"Черновая башня совместима только с относительной экструзии. На принтерах " -"BambuLab она всегда включена (флажок стоит).\n" -"Некоторые экструдеры работают лучше при отключении этой опции (абсолютный " -"режим экструзии)." +"Черновая башня совместима только с относительной экструзии. На принтерах BambuLab она всегда включена (флажок стоит).\n" +"Некоторые экструдеры работают лучше при отключении этой опции (абсолютный режим экструзии)." -msgid "" -"Classic wall generator produces walls with constant extrusion width and for " -"very thin areas is used gap-fill. Arachne engine produces walls with " -"variable extrusion width" -msgstr "" -"Движок классического генератора периметров создаёт их с постоянной шириной " -"экструзии, а для очень тонких участков используется параметр «Заполнение " -"пробелов». Движок Arachne же создает периметры с переменной шириной " -"экструзии." +msgid "Classic wall generator produces walls with constant extrusion width and for very thin areas is used gap-fill. Arachne engine produces walls with variable extrusion width" +msgstr "Движок классического генератора периметров создаёт их с постоянной шириной экструзии, а для очень тонких участков используется параметр «Заполнение пробелов». Движок Arachne же создает периметры с переменной шириной экструзии." msgid "Classic" msgstr "Классический" @@ -11068,118 +8507,56 @@ msgstr "Arachne" msgid "Wall transition length" msgstr "Длина перехода к периметру" -msgid "" -"When transitioning between different numbers of walls as the part becomes " -"thinner, a certain amount of space is allotted to split or join the wall " -"segments. It's expressed as a percentage over nozzle diameter" -msgstr "" -"При переходе между разным количеством периметров по мере того, как деталь " -"становится тоньше, выделяется определенное пространство для разделения или " -"соединения линий периметров. Выражается в процентах от диаметра сопла." +msgid "When transitioning between different numbers of walls as the part becomes thinner, a certain amount of space is allotted to split or join the wall segments. It's expressed as a percentage over nozzle diameter" +msgstr "При переходе между разным количеством периметров по мере того, как деталь становится тоньше, выделяется определенное пространство для разделения или соединения линий периметров. Выражается в процентах от диаметра сопла." msgid "Wall transitioning filter margin" msgstr "Поле фильтра при переходе между периметрами" msgid "" -"Prevent transitioning back and forth between one extra wall and one less. " -"This margin extends the range of extrusion widths which follow to [Minimum " -"wall width - margin, 2 * Minimum wall width + margin]. Increasing this " -"margin reduces the number of transitions, which reduces the number of " -"extrusion starts/stops and travel time. However, large extrusion width " -"variation can lead to under- or overextrusion problems. It's expressed as a " -"percentage over nozzle diameter" +"Prevent transitioning back and forth between one extra wall and one less. This margin extends the range of extrusion widths which follow to [Minimum wall width - margin, 2 * Minimum wall width + margin]. Increasing this margin reduces the number of transitions, which reduces the number of extrusion starts/stops and travel time. However, large extrusion width variation can lead to under- or " +"overextrusion problems. It's expressed as a percentage over nozzle diameter" msgstr "" -"Предотвращает переход туда и обратно между одним лишним периметром и одним " -"недостающим. Это поле расширяет диапазон значений ширины экструзии, который " -"определяется как [Минимальная ширина периметра - Поле, 2 * Минимальная " -"ширина периметра + Поле]. Расширение этого поля позволяет сократить " -"количество переходов, что в свою очередь позволяет сократить количество " -"запусков/остановок экструдирования и время перемещения. Однако большой " -"разброс значений ширины экструзии может привести к проблемам недо/" -"переэкструзии материала. Если задано в процентах, то расчёт производится " -"относительно диаметра сопла." +"Предотвращает переход туда и обратно между одним лишним периметром и одним недостающим. Это поле расширяет диапазон значений ширины экструзии, который определяется как [Минимальная ширина периметра - Поле, 2 * Минимальная ширина периметра + Поле]. Расширение этого поля позволяет сократить количество переходов, что в свою очередь позволяет сократить количество запусков/остановок экструдирования " +"и время перемещения. Однако большой разброс значений ширины экструзии может привести к проблемам недо/переэкструзии материала. Если задано в процентах, то расчёт производится относительно диаметра сопла." msgid "Wall transitioning threshold angle" msgstr "Пороговый угол перехода между периметрами" -msgid "" -"When to create transitions between even and odd numbers of walls. A wedge " -"shape with an angle greater than this setting will not have transitions and " -"no walls will be printed in the center to fill the remaining space. Reducing " -"this setting reduces the number and length of these center walls, but may " -"leave gaps or overextrude" +msgid "When to create transitions between even and odd numbers of walls. A wedge shape with an angle greater than this setting will not have transitions and no walls will be printed in the center to fill the remaining space. Reducing this setting reduces the number and length of these center walls, but may leave gaps or overextrude" msgstr "" -"Когда требуется создавать переходы между чётным и нечётным количеством " -"периметров. Клиновидная форма с углом, превышающим этот параметр, не будет " -"иметь переходов, и периметры не будут напечатаны в центре для заполнения " -"оставшегося пространства. Уменьшение значения этого параметра позволяет " -"сократить количество и длину этих центральных периметров, но при этом могут " -"остаться зазоры или произойти чрезмерное экструдирование." +"Когда требуется создавать переходы между чётным и нечётным количеством периметров. Клиновидная форма с углом, превышающим этот параметр, не будет иметь переходов, и периметры не будут напечатаны в центре для заполнения оставшегося пространства. Уменьшение значения этого параметра позволяет сократить количество и длину этих центральных периметров, но при этом могут остаться зазоры или произойти " +"чрезмерное экструдирование." msgid "Wall distribution count" msgstr "Счётчик распределений по периметрам" -msgid "" -"The number of walls, counted from the center, over which the variation needs " -"to be spread. Lower values mean that the outer walls don't change in width" -msgstr "" -"Количество периметров, отсчитываемое от центра, на которые необходимо " -"распространить изменения. Более низкое значение означает, что ширина внешних " -"периметров не изменяется." +msgid "The number of walls, counted from the center, over which the variation needs to be spread. Lower values mean that the outer walls don't change in width" +msgstr "Количество периметров, отсчитываемое от центра, на которые необходимо распространить изменения. Более низкое значение означает, что ширина внешних периметров не изменяется." msgid "Minimum feature size" msgstr "Минимальный размер элемента" -msgid "" -"Minimum thickness of thin features. Model features that are thinner than " -"this value will not be printed, while features thicker than the Minimum " -"feature size will be widened to the Minimum wall width. It's expressed as a " -"percentage over nozzle diameter" -msgstr "" -"Минимальная толщина тонких элементов. Элементы модели, которые тоньше этого " -"значения, не будут напечатаны, в то время как элементы, толщина которых " -"превышает «Минимальный размер элемента», будут расширены до минимальной " -"ширины периметра. Выражается в процентах от диаметра сопла." +msgid "Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker than the Minimum feature size will be widened to the Minimum wall width. It's expressed as a percentage over nozzle diameter" +msgstr "Минимальная толщина тонких элементов. Элементы модели, которые тоньше этого значения, не будут напечатаны, в то время как элементы, толщина которых превышает «Минимальный размер элемента», будут расширены до минимальной ширины периметра. Выражается в процентах от диаметра сопла." msgid "First layer minimum wall width" msgstr "Минимальная ширина периметра первого слоя" -msgid "" -"The minimum wall width that should be used for the first layer is " -"recommended to be set to the same size as the nozzle. This adjustment is " -"expected to enhance adhesion." -msgstr "" -"Минимальная ширина периметра, используемая для печати первого слоя. Значение " -"рекомендуется устанавливать равным диаметру сопла. Ожидается, что такая " -"регулировка повышает адгезию." +msgid "The minimum wall width that should be used for the first layer is recommended to be set to the same size as the nozzle. This adjustment is expected to enhance adhesion." +msgstr "Минимальная ширина периметра, используемая для печати первого слоя. Значение рекомендуется устанавливать равным диаметру сопла. Ожидается, что такая регулировка повышает адгезию." msgid "Minimum wall width" msgstr "Минимальная ширина периметра" -msgid "" -"Width of the wall that will replace thin features (according to the Minimum " -"feature size) of the model. If the Minimum wall width is thinner than the " -"thickness of the feature, the wall will become as thick as the feature " -"itself. It's expressed as a percentage over nozzle diameter" -msgstr "" -"Ширина периметра, которая заменит тонкие элементы (в соответствии с " -"минимальным размера элемента) модели. Если минимальная ширина периметра " -"меньше толщины элемента, толщина периметра будет приведена к толщине самого " -"элемента. Если задано в процентах, то значение вычисляться относительно " -"диаметра сопла." +msgid "Width of the wall that will replace thin features (according to the Minimum feature size) of the model. If the Minimum wall width is thinner than the thickness of the feature, the wall will become as thick as the feature itself. It's expressed as a percentage over nozzle diameter" +msgstr "Ширина периметра, которая заменит тонкие элементы (в соответствии с минимальным размера элемента) модели. Если минимальная ширина периметра меньше толщины элемента, толщина периметра будет приведена к толщине самого элемента. Если задано в процентах, то значение вычисляться относительно диаметра сопла." msgid "Detect narrow internal solid infill" msgstr "Обнаруживать узкую область сплошного заполнения" -msgid "" -"This option will auto detect narrow internal solid infill area. If enabled, " -"concentric pattern will be used for the area to speed printing up. " -"Otherwise, rectilinear pattern is used defaultly." -msgstr "" -"Этот параметр автоматически определяет узкую внутреннюю область сплошного " -"заполнения. Если включено, для ускорения печати будет использоваться " -"концентрический шаблон. В противном случае по умолчанию используется " -"прямолинейный шаблон." +msgid "This option will auto detect narrow internal solid infill area. If enabled, concentric pattern will be used for the area to speed printing up. Otherwise, rectilinear pattern is used defaultly." +msgstr "Этот параметр автоматически определяет узкую внутреннюю область сплошного заполнения. Если включено, для ускорения печати будет использоваться концентрический шаблон. В противном случае по умолчанию используется прямолинейный шаблон." msgid "invalid value " msgstr "недопустимое значение " @@ -11242,12 +8619,6 @@ msgstr "Загрузка материалов по умолчанию" msgid "Load first filament as default for those not loaded" msgstr "Использовать первый материал по умолчанию, если не загружен другой" -msgid "Minimum save" -msgstr "Минимальное сохранение" - -msgid "export 3mf with minimum size." -msgstr "экспорт 3mf файла с минимальным размером." - msgid "mtcpp" msgstr "mtcpp" @@ -11264,9 +8635,7 @@ msgid "No check" msgstr "Без проверки" msgid "Do not run any validity checks, such as gcode path conflicts check." -msgstr "" -"Не запускать никакие проверки валидности, такие как проверка на конфликт " -"путей в G-коде." +msgstr "Не запускать никакие проверки валидности, такие как проверка на конфликт путей в G-коде." msgid "Normative check" msgstr "Нормативная проверка" @@ -11296,8 +8665,7 @@ msgid "Arrange Options" msgstr "Параметры расстановки" msgid "Arrange options: 0-disable, 1-enable, others-auto" -msgstr "" -"Параметры расстановки: 0 - отключить, 1 - включить, другие - автоматически" +msgstr "Параметры расстановки: 0 - отключить, 1 - включить, другие - автоматически" # ??? msgid "Repetions count" @@ -11307,42 +8675,14 @@ msgstr "Количество повторений" msgid "Repetions count of the whole model" msgstr "Количество повторений для всей модели" -msgid "Ensure on bed" -msgstr "Обеспечивать размещение на столе" - -msgid "" -"Lift the object above the bed when it is partially below. Disabled by default" -msgstr "" -"Поднимает модель над столом, когда она частично находится ниже его уровня. " -"По умолчанию отключено." - msgid "Convert Unit" msgstr "Преобразовать единицу измерения" msgid "Convert the units of model" msgstr "Преобразование единиц измерения модели" -msgid "Orient Options" -msgstr "Параметры ориентации" - -msgid "Orient options: 0-disable, 1-enable, others-auto" -msgstr "" -"Параметры ориентации: 0 - отключить, 1 - включить, другие - автоматически" - -msgid "Rotation angle around the Z axis in degrees." -msgstr "Угол поворота вокруг оси Z в градусах." - -msgid "Rotate around X" -msgstr "Поворот вокруг оси X" - -msgid "Rotation angle around the X axis in degrees." -msgstr "Угол поворота вокруг оси X в градусах." - -msgid "Rotate around Y" -msgstr "Поворот вокруг оси Y" - -msgid "Rotation angle around the Y axis in degrees." -msgstr "Угол поворота вокруг оси Y в градусах." +msgid "Orient the model" +msgstr "Ориентация модели" msgid "Scale the model by a float factor" msgstr "Масштабирование модели с помощью коэффициента." @@ -11366,28 +8706,16 @@ msgid "Skip some objects in this print" msgstr "Пропустить некоторые модели в этом печати" msgid "load uptodate process/machine settings when using uptodate" -msgstr "" -"Загрузить последние настройки процесса/принтера при использовании актуальной " -"версии" +msgstr "Загрузить последние настройки процесса/принтера при использовании актуальной версии" -msgid "" -"load uptodate process/machine settings from the specified file when using " -"uptodate" -msgstr "" -"Загружать последние настройки процесса/принтера из указанного файла при " -"использовании актуальной версии" +msgid "load uptodate process/machine settings from the specified file when using uptodate" +msgstr "Загружать последние настройки процесса/принтера из указанного файла при использовании актуальной версии" msgid "Data directory" msgstr "Папка конфигурации пользователя" -msgid "" -"Load and store settings at the given directory. This is useful for " -"maintaining different profiles or including configurations from a network " -"storage." -msgstr "" -"Загрузка и сохранение настроек будет производиться в заданную папку. Это " -"полезно для сохранения различных профилей или конфигураций из сетевого " -"хранилища." +msgid "Load and store settings at the given directory. This is useful for maintaining different profiles or including configurations from a network storage." +msgstr "Загрузка и сохранение настроек будет производиться в заданную папку. Это полезно для сохранения различных профилей или конфигураций из сетевого хранилища." msgid "Output directory" msgstr "Папка для сохранения" @@ -11398,19 +8726,8 @@ msgstr "Папка для сохранения экспортируемых фа msgid "Debug level" msgstr "Уровень отладки" -msgid "" -"Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:" -"trace\n" -msgstr "" -"Задаёт параметр чувствительности записи событий в журнал. \\\"0: " -"Неустранимая ошибка, 1: Ошибка, 2: Предупреждение, 3: Информация, 4: " -"Отладка, 5: Трассировка\n" - -msgid "Load custom gcode" -msgstr "Загрузить пользовательский G-код" - -msgid "Load custom gcode from json" -msgstr "Загрузить пользовательской G-код из json" +msgid "Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n" +msgstr "Задаёт параметр чувствительности записи событий в журнал. \\\"0: Неустранимая ошибка, 1: Ошибка, 2: Предупреждение, 3: Информация, 4: Отладка, 5: Трассировка\n" msgid "Error in zip archive" msgstr "Ошибка с zip-архивом" @@ -11443,9 +8760,7 @@ msgid "large overhangs" msgstr "большая области нависания" #, c-format, boost-format -msgid "" -"It seems object %s has %s. Please re-orient the object or enable support " -"generation." +msgid "It seems object %s has %s. Please re-orient the object or enable support generation." msgstr "" "Похоже, что у модели %s имеются замечания - %s. \n" "Переориентируйте её или включите генерацию поддержки." @@ -11453,25 +8768,24 @@ msgstr "" msgid "Optimizing toolpath" msgstr "Оптимизация траектории инструмента" +msgid "Empty layers around bottom are replaced by nearest normal layers." +msgstr "Пустые слои обнаруженные на дне модели были заменены ближайшими нормальными слоями." + +msgid "The model has too many empty layers." +msgstr "Модель имеет слишком много пустых слоев." + msgid "Slicing mesh" msgstr "Нарезка сетки" -msgid "" -"No layers were detected. You might want to repair your STL file(s) or check " -"their size or thickness and retry.\n" -msgstr "" -"Слоёв не обнаружено. Возможно, требуется починить STL файл(ы) или проверить " -"размер/толщину и повторить попытку.\n" +msgid "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n" +msgstr "Слоёв не обнаружено. Возможно, требуется починить STL файл(ы) или проверить размер/толщину и повторить попытку.\n" msgid "" -"An object's XY size compensation will not be used because it is also color-" -"painted.\n" +"An object's XY size compensation will not be used because it is also color-painted.\n" "XY Size compensation can not be combined with color-painting." msgstr "" -"Коррекция горизонтальных размеров модели не будет действовать, поскольку для " -"этой модели была выполнена операция окрашивания.\n" -"Коррекция горизонтальных размеров модели не может использоваться в сочетании " -"с функцией раскрашивания." +"Коррекция горизонтальных размеров модели не будет действовать, поскольку для этой модели была выполнена операция окрашивания.\n" +"Коррекция горизонтальных размеров модели не может использоваться в сочетании с функцией раскрашивания." #, c-format, boost-format msgid "Support: generate toolpath at layer %d" @@ -11504,11 +8818,8 @@ msgstr "Поддержка: ремонт отверстий на слое %d" msgid "Support: propagate branches at layer %d" msgstr "Поддержка: построение ветвей на слое %d" -msgid "" -"Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension." -msgstr "" -"Неизвестный формат файла. Входной файл должен иметь расширение *.stl, *.obj, " -"*.amf(.xml)." +msgid "Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension." +msgstr "Неизвестный формат файла. Входной файл должен иметь расширение *.stl, *.obj, *.amf(.xml)." msgid "Loading of a model file failed." msgstr "Не удалось загрузить файл модели." @@ -11517,9 +8828,7 @@ msgid "The supplied file couldn't be read because it's empty" msgstr "Предоставленный файл не может быть прочитан, так как он пуст." msgid "Unknown file format. Input file must have .3mf or .zip.amf extension." -msgstr "" -"Неизвестный формат файла. Входной файл должен иметь расширение *.3mf или *." -"zip.amf." +msgstr "Неизвестный формат файла. Входной файл должен иметь расширение *.3mf или *.zip.amf." msgid "Canceled" msgstr "Отменено" @@ -11578,10 +8887,8 @@ msgstr "Вики-сайт" msgid "How to use calibration result?" msgstr "Как использовать результаты калибровки?" -msgid "" -"You could change the Flow Dynamics Calibration Factor in material editing" -msgstr "" -"Коэффициент калибровки динамики потока можно изменить в настройках материала." +msgid "You could change the Flow Dynamics Calibration Factor in material editing" +msgstr "Коэффициент калибровки динамики потока можно изменить в настройках материала." msgid "" "The current firmware version of the printer does not support calibration.\n" @@ -11608,20 +8915,6 @@ msgstr "Введите имя, который хотите сохранить н msgid "The name cannot exceed 40 characters." msgstr "Максимальная длина имени 40 символов." -#, c-format, boost-format -msgid "" -"Please input valid values:\n" -"Start value: >= %.1f\n" -"End value: <= %.1f\n" -"End value: > Start value\n" -"Value step: >= %.3f)" -msgstr "" -"Введите допустимое значение:\n" -"Начальное: >= %.1f\n" -"Конечное: <= %.1f\n" -"Конечное: > Начальное\n" -"Шаг: >= %.3f)" - msgid "The name cannot be empty." msgstr "Имя не может быть пустым." @@ -11638,11 +8931,8 @@ msgstr "Имя совпадает с именем другого существ msgid "create new preset failed." msgstr "не удалось создать новый профиль." -msgid "" -"Are you sure to cancel the current calibration and return to the home page?" -msgstr "" -"Вы уверены, что хотите отменить текущую калибровку и вернуться на главную " -"страницу?" +msgid "Are you sure to cancel the current calibration and return to the home page?" +msgstr "Вы уверены, что хотите отменить текущую калибровку и вернуться на главную страницу?" msgid "No Printer Connected!" msgstr "Принтер не подключён!" @@ -11653,9 +8943,6 @@ msgstr "Принтер ещё не подключен." msgid "Please select filament to calibrate." msgstr "Пожалуйста, выберите пруток для калибровки." -msgid "The input value size must be 3." -msgstr "Размер входного значения должен быть равен 3." - msgid "Connecting to printer..." msgstr "Подключение к принтеру..." @@ -11674,6 +8961,9 @@ msgstr "Выберите хотя бы один пруток для калибр msgid "Flow rate calibration result has been saved to preset" msgstr "Результат калибровки динамики потока был сохранён в профиль" +msgid "The input value size must be 3." +msgstr "Размер входного значения должен быть равен 3." + msgid "Max volumetric speed calibration result has been saved to preset" msgstr "Результат калибровки максимальной объёмной скорости сохранен в профиль" @@ -11681,24 +8971,15 @@ msgid "When do you need Flow Dynamics Calibration" msgstr "В каких случаях необходима калибровка динамики потока" msgid "" -"We now have added the auto-calibration for different filaments, which is " -"fully automated and the result will be saved into the printer for future " -"use. You only need to do the calibration in the following limited cases:\n" -"1. If you introduce a new filament of different brands/models or the " -"filament is damp;\n" +"We now have added the auto-calibration for different filaments, which is fully automated and the result will be saved into the printer for future use. You only need to do the calibration in the following limited cases:\n" +"1. If you introduce a new filament of different brands/models or the filament is damp;\n" "2. if the nozzle is worn out or replaced with a new one;\n" -"3. If the max volumetric speed or print temperature is changed in the " -"filament setting." +"3. If the max volumetric speed or print temperature is changed in the filament setting." msgstr "" -"Мы добавили функцию автоматической калибровки для различных материалов, " -"которая полностью автоматизирована, а результат калибровки сохраняется в " -"принтере для дальнейшего использования. Калибровка требуется только в " -"следующих ограниченных случаях:\n" -"1. При использовании нового материала другого производителя/типа или при " -"отсыревании материала;\n" +"Мы добавили функцию автоматической калибровки для различных материалов, которая полностью автоматизирована, а результат калибровки сохраняется в принтере для дальнейшего использования. Калибровка требуется только в следующих ограниченных случаях:\n" +"1. При использовании нового материала другого производителя/типа или при отсыревании материала;\n" "2. При износе сопла или его замене на новое;\n" -"3. При изменении в настройках материала максимальной объёмной скорости или " -"температуры печати." +"3. При изменении в настройках материала максимальной объёмной скорости или температуры печати." msgid "About this calibration" msgstr "О данном виде калибровки" @@ -11706,129 +8987,64 @@ msgstr "О данном виде калибровки" msgid "" "Please find the details of Flow Dynamics Calibration from our wiki.\n" "\n" -"Usually the calibration is unnecessary. When you start a single color/" -"material print, with the \"flow dynamics calibration\" option checked in the " -"print start menu, the printer will follow the old way, calibrate the " -"filament before the print; When you start a multi color/material print, the " -"printer will use the default compensation parameter for the filament during " -"every filament switch which will have a good result in most cases.\n" +"Usually the calibration is unnecessary. When you start a single color/material print, with the \"flow dynamics calibration\" option checked in the print start menu, the printer will follow the old way, calibrate the filament before the print; When you start a multi color/material print, the printer will use the default compensation parameter for the filament during every filament switch which " +"will have a good result in most cases.\n" "\n" -"Please note there are a few cases that will make the calibration result not " -"reliable: using a texture plate to do the calibration; the build plate does " -"not have good adhesion (please wash the build plate or apply gluestick!) ..." -"You can find more from our wiki.\n" +"Please note there are a few cases that will make the calibration result not reliable: using a texture plate to do the calibration; the build plate does not have good adhesion (please wash the build plate or apply gluestick!) ...You can find more from our wiki.\n" "\n" -"The calibration results have about 10 percent jitter in our test, which may " -"cause the result not exactly the same in each calibration. We are still " -"investigating the root cause to do improvements with new updates." +"The calibration results have about 10 percent jitter in our test, which may cause the result not exactly the same in each calibration. We are still investigating the root cause to do improvements with new updates." msgstr "" -"Подробную информацию про калибровку динамики потока можно найти на нашем " -"вики-сайте.\n" +"Подробную информацию про калибровку динамики потока можно найти на нашем вики-сайте.\n" "\n" "При обычных обстоятельствах калибровка не требуется. \n" -"Если при запуске печати одним цветом/материалом в меню запуска печати " -"отмечена опция «Калибровка динамики потока», то калибровка пластиковой нити " -"будет производится старым способом. \n" -"При запуске печати несколькими цветами/материалами, принтер будет " -"использовать параметр компенсации по умолчанию для материала при каждой его " -"смене, что в большинстве случаев позволяет получить хороший результат.\n" +"Если при запуске печати одним цветом/материалом в меню запуска печати отмечена опция «Калибровка динамики потока», то калибровка пластиковой нити будет производится старым способом. \n" +"При запуске печати несколькими цветами/материалами, принтер будет использовать параметр компенсации по умолчанию для материала при каждой его смене, что в большинстве случаев позволяет получить хороший результат.\n" "\n" -"Обратите внимание, что есть несколько случаев, когда результат калибровки " -"будет недостоверным. Это использование для калибровки текстурированной " -"печатной пластины и когда у печатной пластины плохая адгезия с материалом. " -"Более подробную информацию можно найти на нашем вики-сайте.\n" +"Обратите внимание, что есть несколько случаев, когда результат калибровки будет недостоверным. Это использование для калибровки текстурированной печатной пластины и когда у печатной пластины плохая адгезия с материалом. Более подробную информацию можно найти на нашем вики-сайте.\n" "\n" -"По нашим тестам, результаты калибровки имеют погрешность примерно 10%, что " -"может приводить к разным результатам при каждой калибровке. Мы продолжаем " -"выяснять причину, чтобы улучшить ситуацию в новых обновлениях." +"По нашим тестам, результаты калибровки имеют погрешность примерно 10%, что может приводить к разным результатам при каждой калибровке. Мы продолжаем выяснять причину, чтобы улучшить ситуацию в новых обновлениях." msgid "When to use Flow Rate Calibration" msgstr "В каких случаях необходима калибровка скорости потока" msgid "" -"After using Flow Dynamics Calibration, there might still be some extrusion " -"issues, such as:\n" -"1. Over-Extrusion: Excess material on your printed object, forming blobs or " -"zits, or the layers seem thicker than expected and not uniform.\n" -"2. Under-Extrusion: Very thin layers, weak infill strength, or gaps in the " -"top layer of the model, even when printing slowly.\n" +"After using Flow Dynamics Calibration, there might still be some extrusion issues, such as:\n" +"1. Over-Extrusion: Excess material on your printed object, forming blobs or zits, or the layers seem thicker than expected and not uniform.\n" +"2. Under-Extrusion: Very thin layers, weak infill strength, or gaps in the top layer of the model, even when printing slowly.\n" "3. Poor Surface Quality: The surface of your prints seems rough or uneven.\n" -"4. Weak Structural Integrity: Prints break easily or don't seem as sturdy as " -"they should be." +"4. Weak Structural Integrity: Prints break easily or don't seem as sturdy as they should be." msgstr "" -"После проведения калибровки динамики потока всё ещё могут возникать " -"некоторые проблемы с экструзией, такие как:\n" -"1. Избыточная экструзия. Это приводит к образованию на модели капель или " -"сгустков, слои кажутся толще и неравномерными, чем ожидалось.\n" -"2. Недоэкструзия. Очень тонкие слои, слабая прочность заполнения или пробелы " -"на верхнем слое модели, даже при медленной печати.\n" -"3. Низкое качество поверхности. Поверхность деталей кажется шероховатой или " -"неровной.\n" -"4. Слабая конструкционная прочность. Напечатанное легко ломается или кажется " -"не таким прочным, как должно быть." +"После проведения калибровки динамики потока всё ещё могут возникать некоторые проблемы с экструзией, такие как:\n" +"1. Избыточная экструзия. Это приводит к образованию на модели капель или сгустков, слои кажутся толще и неравномерными, чем ожидалось.\n" +"2. Недоэкструзия. Очень тонкие слои, слабая прочность заполнения или пробелы на верхнем слое модели, даже при медленной печати.\n" +"3. Низкое качество поверхности. Поверхность деталей кажется шероховатой или неровной.\n" +"4. Слабая конструкционная прочность. Напечатанное легко ломается или кажется не таким прочным, как должно быть." + +msgid "In addition, Flow Rate Calibration is crucial for foaming materials like LW-PLA used in RC planes. These materials expand greatly when heated, and calibration provides a useful reference flow rate." +msgstr "Кроме того, калибровка скорости потока крайне важна для вспенивающихся материалов, таких как LW-PLA, используемых при печати деталей для радиоуправляемых самолетов. Эти материалы сильно расширяются при нагревании, а калибровка позволяет получить эталонную скорости потока." msgid "" -"In addition, Flow Rate Calibration is crucial for foaming materials like LW-" -"PLA used in RC planes. These materials expand greatly when heated, and " -"calibration provides a useful reference flow rate." +"Flow Rate Calibration measures the ratio of expected to actual extrusion volumes. The default setting works well in Bambu Lab printers and official filaments as they were pre-calibrated and fine-tuned. For a regular filament, you usually won't need to perform a Flow Rate Calibration unless you still see the listed defects after you have done other calibrations. For more details, please check " +"out the wiki article." msgstr "" -"Кроме того, калибровка скорости потока крайне важна для вспенивающихся " -"материалов, таких как LW-PLA, используемых при печати деталей для " -"радиоуправляемых самолетов. Эти материалы сильно расширяются при нагревании, " -"а калибровка позволяет получить эталонную скорости потока." +"Калибровка скорости потока измеряет соотношение ожидаемого и фактического объёмов экструзии. На принтерах Bambu Lab с официальными материалами, стандартные настройки работают хорошо, так как они были предварительно откалиброваны и тщательно настроены. Для обычного материала обычно не требуется выполнять калибровку скорости потока, если только после выполнения других калибровок вы всё ещё видите " +"перечисленные дефекты. Более подробную информацию можно найти на нашем вики-сайте." msgid "" -"Flow Rate Calibration measures the ratio of expected to actual extrusion " -"volumes. The default setting works well in Bambu Lab printers and official " -"filaments as they were pre-calibrated and fine-tuned. For a regular " -"filament, you usually won't need to perform a Flow Rate Calibration unless " -"you still see the listed defects after you have done other calibrations. For " -"more details, please check out the wiki article." +"Auto Flow Rate Calibration utilizes Bambu Lab's Micro-Lidar technology, directly measuring the calibration patterns. However, please be advised that the efficacy and accuracy of this method may be compromised with specific types of materials. Particularly, filaments that are transparent or semi-transparent, sparkling-particled, or have a high-reflective finish may not be suitable for this " +"calibration and can produce less-than-desirable results.\n" +"\n" +"The calibration results may vary between each calibration or filament. We are still improving the accuracy and compatibility of this calibration through firmware updates over time.\n" +"\n" +"Caution: Flow Rate Calibration is an advanced process, to be attempted only by those who fully understand its purpose and implications. Incorrect usage can lead to sub-par prints or printer damage. Please make sure to carefully read and understand the process before doing it." msgstr "" -"Калибровка скорости потока измеряет соотношение ожидаемого и фактического " -"объёмов экструзии. На принтерах Bambu Lab с официальными материалами, " -"стандартные настройки работают хорошо, так как они были предварительно " -"откалиброваны и тщательно настроены. Для обычного материала обычно не " -"требуется выполнять калибровку скорости потока, если только после выполнения " -"других калибровок вы всё ещё видите перечисленные дефекты. Более подробную " -"информацию можно найти на нашем вики-сайте." - -msgid "" -"Auto Flow Rate Calibration utilizes Bambu Lab's Micro-Lidar technology, " -"directly measuring the calibration patterns. However, please be advised that " -"the efficacy and accuracy of this method may be compromised with specific " -"types of materials. Particularly, filaments that are transparent or semi-" -"transparent, sparkling-particled, or have a high-reflective finish may not " -"be suitable for this calibration and can produce less-than-desirable " -"results.\n" -"\n" -"The calibration results may vary between each calibration or filament. We " -"are still improving the accuracy and compatibility of this calibration " -"through firmware updates over time.\n" -"\n" -"Caution: Flow Rate Calibration is an advanced process, to be attempted only " -"by those who fully understand its purpose and implications. Incorrect usage " -"can lead to sub-par prints or printer damage. Please make sure to carefully " -"read and understand the process before doing it." -msgstr "" -"Автоматическая калибровка скорости потока использует технологию микролидара " -"Bambu Lab, непосредственно измеряя калибровочные шаблоны. Однако имейте " -"ввиду, что эффективность и точность этого метода может быть снижена при " -"использовании определенных типов материалов. В частности, прозрачные или " -"полупрозрачные материалы, материалы с блестящими частицами или с " -"высокоотражающим покрытием могут не подойти для данной калибровки и привести " -"к нежелательным результатам.\n" +"Автоматическая калибровка скорости потока использует технологию микролидара Bambu Lab, непосредственно измеряя калибровочные шаблоны. Однако имейте ввиду, что эффективность и точность этого метода может быть снижена при использовании определенных типов материалов. В частности, прозрачные или полупрозрачные материалы, материалы с блестящими частицами или с высокоотражающим покрытием могут не " +"подойти для данной калибровки и привести к нежелательным результатам.\n" "\n" "\n" -"Результаты калибровки могут различаться от калибровки к калибровке или от " -"материала к материалу. Мы продолжаем улучшать точность и совместимость этой " -"калибровки путем обновления прошивки принтера.\n" +"Результаты калибровки могут различаться от калибровки к калибровке или от материала к материалу. Мы продолжаем улучшать точность и совместимость этой калибровки путем обновления прошивки принтера.\n" "\n" -"Внимание: калибровка скорости потока - это сложный процесс, к которому " -"следует прибегать только тем, кто полностью понимает её назначение и " -"последствия. Неправильное использование может привести к некачественной " -"печати или повреждению принтера. Пожалуйста, внимательно прочитайте и " -"поймите суть процесса, прежде чем приступать к его выполнению." +"Внимание: калибровка скорости потока - это сложный процесс, к которому следует прибегать только тем, кто полностью понимает её назначение и последствия. Неправильное использование может привести к некачественной печати или повреждению принтера. Пожалуйста, внимательно прочитайте и поймите суть процесса, прежде чем приступать к его выполнению." msgid "When you need Max Volumetric Speed Calibration" msgstr "В каких случаях необходима калибровка максимальной объемной скорости" @@ -11837,13 +9053,10 @@ msgid "Over-extrusion or under extrusion" msgstr "Избыточная или недостаточная экструзия" msgid "Max Volumetric Speed calibration is recommended when you print with:" -msgstr "" -"Калибровка максимальной объёмной скорости рекомендуется при печати с " -"использованием:" +msgstr "Калибровка максимальной объёмной скорости рекомендуется при печати с использованием:" msgid "material with significant thermal shrinkage/expansion, such as..." -msgstr "" -"материалов со значительной термической усадкой/расширением, например..." +msgstr "материалов со значительной термической усадкой/расширением, например..." msgid "materials with inaccurate filament diameter" msgstr "материалов с неточным диаметром пластиковой нити" @@ -11851,46 +9064,25 @@ msgstr "материалов с неточным диаметром пласти msgid "We found the best Flow Dynamics Calibration Factor" msgstr "Мы нашли лучший коэффициент калибровки динамики потока" -msgid "" -"Part of the calibration failed! You may clean the plate and retry. The " -"failed test result would be dropped." -msgstr "" -"Часть калибровки выполнена неудачно! Вы можете очистить печатную пластину и " -"повторить попытку. Результат неудачного теста будет удалён." +msgid "Part of the calibration failed! You may clean the plate and retry. The failed test result would be dropped." +msgstr "Часть калибровки выполнена неудачно! Вы можете очистить печатную пластину и повторить попытку. Результат неудачного теста будет удалён." -msgid "" -"*We recommend you to add brand, materia, type, and even humidity level in " -"the Name" -msgstr "" -"*Мы рекомендуем добавить к названию материала, производителя, тип и даже " -"уровень влажности" +msgid "*We recommend you to add brand, materia, type, and even humidity level in the Name" +msgstr "*Мы рекомендуем добавить к названию материала, производителя, тип и даже уровень влажности" msgid "Failed" msgstr "Неудачно" -msgid "" -"Only one of the results with the same name will be saved. Are you sure you " -"want to overrides the other results?" -msgstr "" -"Будет сохранен только один из результатов с таким же именем. Вы уверены, что " -"хотите перезаписать другие результаты?" +msgid "Only one of the results with the same name will be saved. Are you sure you want to overrides the other results?" +msgstr "Будет сохранен только один из результатов с таким же именем. Вы уверены, что хотите перезаписать другие результаты?" #, c-format, boost-format -msgid "" -"There is already a historical calibration result with the same name: %s. " -"Only one of the results with the same name is saved. Are you sure you want " -"to overrides the historical result?" -msgstr "" -"Результат калибровки с таким именем уже существует: %s. Будет сохранён " -"только один результат с таким же именем. Вы уверены, что хотите перезаписать " -"текущий результат?" +msgid "There is already a historical calibration result with the same name: %s. Only one of the results with the same name is saved. Are you sure you want to overrides the historical result?" +msgstr "Результат калибровки с таким именем уже существует: %s. Будет сохранён только один результат с таким же именем. Вы уверены, что хотите перезаписать текущий результат?" msgid "Please find the best line on your plate" msgstr "Пожалуйста, найдите лучшую линию на столе" -msgid "Please find the cornor with perfect degree of extrusion" -msgstr "Пожалуйста, найдите угол с идеальной степенью экструзии" - msgid "Input Value" msgstr "Входное значение" @@ -11955,12 +9147,8 @@ msgstr "Точная калибровка на основе коэффициен msgid "Title" msgstr "Заголовок" -msgid "" -"A test model will be printed. Please clear the build plate and place it back " -"to the hot bed before calibration." -msgstr "" -"Будет напечатана тестовая модель. Перед калибровкой очистите печатную " -"пластину и установите её обратно на нагреваемый стол." +msgid "A test model will be printed. Please clear the build plate and place it back to the hot bed before calibration." +msgstr "Будет напечатана тестовая модель. Перед калибровкой очистите печатную пластину и установите её обратно на нагреваемый стол." msgid "Printing Parameters" msgstr "Параметры печати" @@ -11990,8 +9178,7 @@ msgid "" msgstr "" "Советы по выбору материала для калибровки: \n" "- Материалы, которые имеют близкие значения температуры нагреваемого стола\n" -"- Различные марки и семейства расходных материалов (Производитель = Bambu, " -"семейство = Basic - базовый, Matte - матовый)" +"- Различные марки и семейства расходных материалов (Производитель = Bambu, семейство = Basic - базовый, Matte - матовый)" msgid "Error desc" msgstr "Описание ошибки" @@ -11999,12 +9186,6 @@ msgstr "Описание ошибки" msgid "Extra info" msgstr "Доп. информация" -msgid "Pattern" -msgstr "Шаблон" - -msgid "Method" -msgstr "Метод" - #, c-format, boost-format msgid "%s is not compatible with %s" msgstr "%s не совместима с %s" @@ -12015,21 +9196,6 @@ msgstr "Автоматическая калибровка динамики по msgid "Connecting to printer" msgstr "Подключением к принтеру" -msgid "From k Value" -msgstr "Начальное значение коэф. K" - -msgid "To k Value" -msgstr "Конечное значение коэф. K" - -msgid "Step value" -msgstr "Шаг" - -msgid "0.5" -msgstr "0.5" - -msgid "0.005" -msgstr "0.005" - msgid "The nozzle diameter has been synchronized from the printer Settings" msgstr "Диаметр сопла был синхронизирован с настройками принтера" @@ -12132,13 +9298,14 @@ msgstr "Удалить исходные" msgid "Send G-Code to printer host" msgstr "Отправить G-кода на хост принтера" +msgid "Send to print" +msgstr "Отправить на печать" + msgid "Upload to Printer Host with the following filename:" msgstr "Загрузить на хост принтера со следующим именем:" msgid "Use forward slashes ( / ) as a directory separator if needed." -msgstr "" -"При необходимости используйте косую черту ( / ) в качестве разделителя " -"каталогов." +msgstr "При необходимости используйте косую черту ( / ) в качестве разделителя каталогов." msgid "Upload to storage" msgstr "Загрузить в хранилище" @@ -12169,6 +9336,12 @@ msgstr "Размер" msgid "Filename" msgstr "Имя файла" +msgid "Message" +msgstr "Сообщение" + +msgid "Error Message" +msgstr "Сообщение об ошибке" + msgid "Cancel selected" msgstr "Отменить выбранное" @@ -12187,6 +9360,9 @@ msgstr "Отмена" msgid "Error uploading to print host" msgstr "Ошибка при отправке на хост печати" +msgid "Error uploading to print host:" +msgstr "Ошибка при отправке на хост печати:" + msgid "PA Calibration" msgstr "Калибровка PA" @@ -12208,6 +9384,9 @@ msgstr "Линии" msgid "PA Pattern" msgstr "Шаблон" +msgid "Method" +msgstr "Метод" + msgid "Start PA: " msgstr "Начальный коэффициент PA: " @@ -12289,14 +9468,12 @@ msgstr "Шаг изменения: " msgid "" "Please input valid values:\n" -"start > 0 \n" -"step >= 0\n" +"start > 0 step >= 0\n" "end > start + step)" msgstr "" -"Введите допустимое значение:\n" -"Начальное > 0\n" -"Шаг >= 0\n" -"Конечное > Начальное + Шаг" +"Введите допустимые значения:\n" +"start > 0 step >= 0\n" +"end > start + step)" msgid "VFA test" msgstr "Тест на вертикальные артефакты (VFA)" @@ -12309,14 +9486,12 @@ msgstr "Конечная скорость: " msgid "" "Please input valid values:\n" -"start > 10 \n" -"step >= 0\n" +"start > 10 step >= 0\n" "end > start + step)" msgstr "" -"Введите допустимое значение:\n" -"Начальное > 10\n" -"Шаг >= 0\n" -"Конечное > Начальное + Шаг" +"Введите допустимые значения:\n" +"start > 10 step >= 0\n" +"end > start + step)" msgid "Start retraction length: " msgstr "Начальная длина отката: " @@ -12345,12 +9520,8 @@ msgstr "Успешно!" msgid "Refresh Printers" msgstr "Обновить принтеры" -msgid "" -"HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" -"signed certificate." -msgstr "" -"Файл корневого сертификата HTTPS не обязателен. Он необходим только при " -"использовании HTTPS с самоподписанным сертификатом." +msgid "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate." +msgstr "Файл корневого сертификата HTTPS не обязателен. Он необходим только при использовании HTTPS с самоподписанным сертификатом." msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgstr "Файлы сертификатов (*.crt, *.pem)|*.crt;*.pem|Все файлы|*.*" @@ -12359,66 +9530,38 @@ msgid "Open CA certificate file" msgstr "Открыть файл корневого сертификата" #, c-format, boost-format -msgid "" -"On this system, %s uses HTTPS certificates from the system Certificate Store " -"or Keychain." -msgstr "" -"В этой системе %s использует HTTPS сертификаты из системного хранилища " -"сертификатов/Keychain." +msgid "On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain." +msgstr "В этой системе %s использует HTTPS сертификаты из системного хранилища сертификатов/Keychain." -msgid "" -"To use a custom CA file, please import your CA file into Certificate Store / " -"Keychain." -msgstr "" -"Чтобы использовать пользовательский файл корневого сертификата, импортируйте " -"его в хранилище сертификатов/Keychain." +msgid "To use a custom CA file, please import your CA file into Certificate Store / Keychain." +msgstr "Чтобы использовать пользовательский файл корневого сертификата, импортируйте его в хранилище сертификатов/Keychain." msgid "Connection to printers connected via the print host failed." -msgstr "" -"Не удалось подключиться к принтерам, подключенным через через хост печати." - -msgid "The start, end or step is not valid value." -msgstr "Недопустимое значение: начальное, конечное или шаг." - -msgid "" -"Unable to calibrate: maybe because the set calibration value range is too " -"large, or the step is too small" -msgstr "" -"Невозможно выполнить калибровку: возможно, установленный диапазон значений " -"калибровки слишком велик или шаг слишком мал." - -msgid "Need select printer" -msgstr "Нужно выбрать принтер" +msgstr "Не удалось подключиться к принтерам, подключенным через через хост печати." #: resources/data/hints.ini: [hint:3D Scene Operations] msgid "" "3D Scene Operations\n" -"Did you know how to control view and object/part selection with mouse and " -"touchpanel in the 3D scene?" +"Did you know how to control view and object/part selection with mouse and touchpanel in the 3D scene?" msgstr "" "Операции с 3D-сценой\n" -"Знаете ли вы, как управлять видом и выбором модели/части с помощью мыши и " -"сенсорной панели в 3D-сцене?" +"Знаете ли вы, как управлять видом и выбором модели/части с помощью мыши и сенсорной панели в 3D-сцене?" #: resources/data/hints.ini: [hint:Cut Tool] msgid "" "Cut Tool\n" -"Did you know that you can cut a model at any angle and position with the " -"cutting tool?" +"Did you know that you can cut a model at any angle and position with the cutting tool?" msgstr "" "Режущий инструмент\n" -"Знаете ли вы, что можно разрезать модель под любым углом с помощью режущего " -"инструмента?" +"Знаете ли вы, что можно разрезать модель под любым углом с помощью режущего инструмента?" #: resources/data/hints.ini: [hint:Fix Model] msgid "" "Fix Model\n" -"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " -"problems?" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing problems?" msgstr "" "Починка модели\n" -"Знаете ли вы, что можно починить повреждённую модель, чтобы избежать " -"множества проблем при нарезке?" +"Знаете ли вы, что можно починить повреждённую модель, чтобы избежать множества проблем при нарезке?" #: resources/data/hints.ini: [hint:Timelapse] msgid "" @@ -12439,196 +9582,140 @@ msgstr "" #: resources/data/hints.ini: [hint:Auto-Orient] msgid "" "Auto-Orient\n" -"Did you know that you can rotate objects to an optimal orientation for " -"printing by a simple click?" +"Did you know that you can rotate objects to an optimal orientation for printing by a simple click?" msgstr "" "Автоориентация\n" -"Знаете ли вы, что можно повернуть модели в оптимальную для печати ориентацию " -"простым щелчком мыши?" +"Знаете ли вы, что можно повернуть модели в оптимальную для печати ориентацию простым щелчком мыши?" #: resources/data/hints.ini: [hint:Lay on Face] msgid "" "Lay on Face\n" -"Did you know that you can quickly orient a model so that one of its faces " -"sits on the print bed? Select the \"Place on face\" function or press the " -"F key." +"Did you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the F key." msgstr "" "Поверхностью на стол\n" -"Знаете ли вы, что можно быстро сориентировать модель так, чтобы одна из её " -"граней лежала на столе? Используйте функцию «Поверхностью на стол» или " -"нажмите клавишу F." +"Знаете ли вы, что можно быстро сориентировать модель так, чтобы одна из её граней лежала на столе? Используйте функцию «Поверхностью на стол» или нажмите клавишу F." #: resources/data/hints.ini: [hint:Object List] msgid "" "Object List\n" -"Did you know that you can view all objects/parts in a list and change " -"settings for each object/part?" +"Did you know that you can view all objects/parts in a list and change settings for each object/part?" msgstr "" "Список моделей\n" -"Знаете ли вы, что можно просматривать все модели/части в списке и изменять " -"настройки для каждой из них?" +"Знаете ли вы, что можно просматривать все модели/части в списке и изменять настройки для каждой из них?" #: resources/data/hints.ini: [hint:Simplify Model] msgid "" "Simplify Model\n" -"Did you know that you can reduce the number of triangles in a mesh using the " -"Simplify mesh feature? Right-click the model and select Simplify model. Read " -"more in the documentation." +"Did you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation." msgstr "" "Упростить сетку модели\n" -"Знаете ли вы, что можно уменьшить количество треугольников в полигональной " -"сетке, используя функцию упрощения сетки? Щелкните правой кнопкой мыши на " -"модели и выберите «Упростить полигональную сетку». Подробнее читайте в " -"документации." +"Знаете ли вы, что можно уменьшить количество треугольников в полигональной сетке, используя функцию упрощения сетки? Щелкните правой кнопкой мыши на модели и выберите «Упростить полигональную сетку». Подробнее читайте в документации." #: resources/data/hints.ini: [hint:Slicing Parameter Table] msgid "" "Slicing Parameter Table\n" -"Did you know that you can view all objects/parts on a table and change " -"settings for each object/part?" +"Did you know that you can view all objects/parts on a table and change settings for each object/part?" msgstr "" "Таблица параметров нарезки\n" -"Знаете ли вы, что можно просмотреть все модели/части в таблице и изменить " -"параметры печати для каждой из них?" +"Знаете ли вы, что можно просмотреть все модели/части в таблице и изменить параметры печати для каждой из них?" #: resources/data/hints.ini: [hint:Split to Objects/Parts] msgid "" "Split to Objects/Parts\n" -"Did you know that you can split a big object into small ones for easy " -"colorizing or printing?" +"Did you know that you can split a big object into small ones for easy colorizing or printing?" msgstr "" "Разделение на модели/части\n" -"Знаете ли вы, что можно разделить большую модель на маленькие для удобства " -"раскрашивания или печати?" +"Знаете ли вы, что можно разделить большую модель на маленькие для удобства раскрашивания или печати?" #: resources/data/hints.ini: [hint:Subtract a Part] msgid "" "Subtract a Part\n" -"Did you know that you can subtract one mesh from another using the Negative " -"part modifier? That way you can, for example, create easily resizable holes " -"directly in Orca Slicer. Read more in the documentation." +"Did you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Orca Slicer. Read more in the documentation." msgstr "" "Вычитание объёмов\n" -"Знаете ли вы, что можно вычесть одну сетку из другой с помощью модификатора " -"«Объём для вычитания»? Таким образом, например, отверстия в модели можно " -"создавать непосредственно в Orca Slicer. Подробнее читайте в документации." +"Знаете ли вы, что можно вычесть одну сетку из другой с помощью модификатора «Объём для вычитания»? Таким образом, например, отверстия в модели можно создавать непосредственно в Orca Slicer. Подробнее читайте в документации." #: resources/data/hints.ini: [hint:STEP] msgid "" "STEP\n" -"Did you know that you can improve your print quality by slicing a STEP file " -"instead of an STL?\n" -"Orca Slicer supports slicing STEP files, providing smoother results than a " -"lower resolution STL. Give it a try!" +"Did you know that you can improve your print quality by slicing a STEP file instead of an STL?\n" +"Orca Slicer supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try!" msgstr "" "STEP\n" -"Знаете ли вы, что можно улучшить качество печати, используя STEP файлы " -"вместо STL?\n" -"Orca Slicer поддерживает нарезку STEP файлов, что обеспечивает более точное " -"представление геометрии, чем при нарезке STL файлов." +"Знаете ли вы, что можно улучшить качество печати, используя STEP файлы вместо STL?\n" +"Orca Slicer поддерживает нарезку STEP файлов, что обеспечивает более точное представление геометрии, чем при нарезке STL файлов." #: resources/data/hints.ini: [hint:Z seam location] msgid "" "Z seam location\n" -"Did you know that you can customize the location of the Z seam, and even " -"paint it on your print, to have it in a less visible location? This improves " -"the overall look of your model. Check it out!" +"Did you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!" msgstr "" "Позиция шва\n" -"Знаете ли вы, что можно изменить расположение шва и даже нарисовать его на " -"модели, чтобы он был менее заметен? Это улучшает общий вид модели. " -"Попробуйте это!" +"Знаете ли вы, что можно изменить расположение шва и даже нарисовать его на модели, чтобы он был менее заметен? Это улучшает общий вид модели. Попробуйте это!" #: resources/data/hints.ini: [hint:Fine-tuning for flow rate] msgid "" "Fine-tuning for flow rate\n" -"Did you know that flow rate can be fine-tuned for even better-looking " -"prints? Depending on the material, you can improve the overall finish of the " -"printed model by doing some fine-tuning." +"Did you know that flow rate can be fine-tuned for even better-looking prints? Depending on the material, you can improve the overall finish of the printed model by doing some fine-tuning." msgstr "" "Точная настройка потока\n" -"Знаете ли вы, что поток можно точно настроить для получения ещё более " -"качественной печати? В зависимости от материала можно внести некоторые " -"корректировки, чтобы улучшить общее качество печати." +"Знаете ли вы, что поток можно точно настроить для получения ещё более качественной печати? В зависимости от материала можно внести некоторые корректировки, чтобы улучшить общее качество печати." #: resources/data/hints.ini: [hint:Split your prints into plates] msgid "" "Split your prints into plates\n" -"Did you know that you can split a model that has a lot of parts into " -"individual plates ready to print? This will simplify the process of keeping " -"track of all the parts." +"Did you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts." msgstr "" "Распределение печатаемого на другие столы\n" -"Знаете ли вы, что модель, состоящую из большого количества частей, можно " -"распределить на несколько столов? Это упрощает процесс отслеживания всех " -"деталей при печати." +"Знаете ли вы, что модель, состоящую из большого количества частей, можно распределить на несколько столов? Это упрощает процесс отслеживания всех деталей при печати." -#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer -#: Height] +#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height] msgid "" "Speed up your print with Adaptive Layer Height\n" -"Did you know that you can print a model even faster, by using the Adaptive " -"Layer Height option? Check it out!" +"Did you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!" msgstr "" -"Ускорение печати с помощью функции «Переменная высота слоёв»\n" -"Знаете ли вы, что можно печатать ещё быстрее, используя функцию «Переменная " -"высота слоёв». Попробуйте!" +"Ускорение печати с функцией «Переменная высота слоёв»\n" +"Знаете ли вы, что можно печатать ещё быстрее, используя функцию «Переменная высота слоёв». Попробуйте!" #: resources/data/hints.ini: [hint:Support painting] msgid "" "Support painting\n" -"Did you know that you can paint the location of your supports? This feature " -"makes it easy to place the support material only on the sections of the " -"model that actually need it." +"Did you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it." msgstr "" "Рисование поддержек\n" -"Знаете ли вы, что можно прямо на модели рисовать где будет размещаться " -"принудительная поддержка, а где поддержка будет заблокирована? Используйте " -"для этого функцию «Рисование поддержек»." +"Знаете ли вы, что можно прямо на модели рисовать где будет размещаться принудительная поддержка, а где поддержка будет заблокирована? Используйте для этого функцию «Рисование поддержек»." #: resources/data/hints.ini: [hint:Different types of supports] msgid "" "Different types of supports\n" -"Did you know that you can choose from multiple types of supports? Tree " -"supports work great for organic models, while saving filament and improving " -"print speed. Check them out!" +"Did you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!" msgstr "" "Различные типы поддержек\n" -"Знаете ли вы, что можно выбрать один из нескольких типов поддержек? " -"Древовидная поддержка отлично подходит для органических моделей, экономя при " -"этом материал, уменьшая время печати." +"Знаете ли вы, что можно выбрать один из нескольких типов поддержек? Древовидная поддержка отлично подходит для органических моделей, экономя при этом материал, уменьшая время печати." #: resources/data/hints.ini: [hint:Printing Silk Filament] msgid "" "Printing Silk Filament\n" -"Did you know that Silk filament needs special consideration to print it " -"successfully? Higher temperature and lower speed are always recommended for " -"the best results." +"Did you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results." msgstr "" "Печать блестящей пластиковой нитью\n" -"Знаете ли вы, что блестящая пластиковая нить требует особого внимания для " -"успешной печати? Для достижения наилучшего результата рекомендуется более " -"высокая температура и более низкая скорость печати." +"Знаете ли вы, что блестящая пластиковая нить требует особого внимания для успешной печати? Для достижения наилучшего результата рекомендуется более высокая температура и более низкая скорость печати." #: resources/data/hints.ini: [hint:Brim for better adhesion] msgid "" "Brim for better adhesion\n" -"Did you know that when printing models have a small contact interface with " -"the printing surface, it's recommended to use a brim?" +"Did you know that when printing models have a small contact interface with the printing surface, it's recommended to use a brim?" msgstr "" "Кайма для лучшей адгезии\n" -"Знаете ли вы, что при печати модели имеющей небольшой контакт с поверхностью " -"стола, рекомендуется использовать кайму?" +"Знаете ли вы, что при печати модели имеющей небольшой контакт с поверхностью стола, рекомендуется использовать кайму?" #: resources/data/hints.ini: [hint:Set parameters for multiple objects] msgid "" "Set parameters for multiple objects\n" -"Did you know that you can set slicing parameters for all selected objects at " -"one time?" +"Did you know that you can set slicing parameters for all selected objects at one time?" msgstr "" "Задание параметров для нескольких моделей\n" -"Знаете ли вы, что можно задать параметры нарезки сразу для всех выбранных " -"моделей?" +"Знаете ли вы, что можно задать параметры нарезки сразу для всех выбранных моделей?" #: resources/data/hints.ini: [hint:Stack objects] msgid "" @@ -12636,663 +9723,1217 @@ msgid "" "Did you know that you can stack objects as a whole one?" msgstr "" "Объединение моделей\n" -"Знаете ли вы, что можно объединить несколько моделей в единую? Используйте " -"для этого команду «Объединить в сборку», выбрав несколько моделей." +"Знаете ли вы, что можно объединить несколько моделей в единую? Используйте для этого команду «Объединить в сборку», выбрав несколько моделей." #: resources/data/hints.ini: [hint:Flush into support/objects/infill] msgid "" "Flush into support/objects/infill\n" -"Did you know that you can save the wasted filament by flushing them into " -"support/objects/infill during filament change?" +"Did you know that you can save the wasted filament by flushing them into support/objects/infill during filament change?" msgstr "" "Очистка в поддержку/модель/заполнение\n" -"Знаете ли вы, что при смене пластиковой нити, можно сохранить материал, " -"который иначе попал бы на черновую башню, сбросив его в поддержку/модель/" -"заполнение?" +"Знаете ли вы, что при смене пластиковой нити, можно сохранить материал, который иначе попал бы на черновую башню, сбросив его в поддержку/модель/заполнение?" #: resources/data/hints.ini: [hint:Improve strength] msgid "" "Improve strength\n" -"Did you know that you can use more wall loops and higher sparse infill " -"density to improve the strength of the model?" +"Did you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?" msgstr "" "Увеличение прочности\n" -"Знаете ли вы, что для повышения прочности модели можно увеличить количество " -"периметров и плотность заполнения?" +"Знаете ли вы, что для повышения прочности модели можно увеличить количество периметров и плотность заполнения?" -#: resources/data/hints.ini: [hint:When need to print with the printer door -#: opened] +msgid "Left Preset Value" +msgstr "Значение в левом профиле" + +msgid "Right Preset Value" +msgstr "Значение в правом профиле" + +msgid "Undef category" +msgstr "Неопределённая категория" + +msgid "Undef group" +msgstr "Неопределённая группа" + +msgid "Retraction Length (Toolchange)" +msgstr "Длина отката (при смене инструмента)" + +msgid "Flow Dynamic" +msgstr "Динамика потока" + +msgid "A fatal error occurred: %1%" +msgstr "Произошла фатальная ошибка: %1%" + +msgid "Record" +msgstr "Запись" + +msgid "Report issue" +msgstr "Сообщить о проблеме" + +msgid "Connection to OctoPrint/Klipper works correctly." +msgstr "Соединение с OctoPrint/Klipper успешно установлено." + +msgid "Could not connect to OctoPrint/Klipper" +msgstr "Не удаётся подключиться к OctoPrint/Klipper" + +msgid "Connection to FlashAir works correctly." +msgstr "Соединение с FlashAir успешно установлено." + +msgid "Could not connect to FlashAir" +msgstr "Не удаётся подключиться к FlashAir" + +msgid "Connection to Duet works correctly." +msgstr "Соединение с Duet успешно установлено." + +msgid "Could not connect to Duet" +msgstr "Не удалось подключиться к Duet" + +msgid "Connection to AstroBox works correctly." +msgstr "Соединение с AstroBox успешно установлено." + +msgid "Could not connect to AstroBox" +msgstr "Не удалось подключиться к AstroBox" + +msgid "Connection to Repetier works correctly." +msgstr "Подключение к Repetier успешно установлено." + +msgid "Could not connect to Repetier" +msgstr "Не удалось подключиться к Repetier" + +msgid "Connection to MKS works correctly." +msgstr "Подключение к MKS успешно установлено." + +msgid "Could not connect to MKS" +msgstr "Не удалось подключиться к MKS" + +msgid "Connection to PrusaLink works correctly." +msgstr "Подключение к PrusaLink установлено." + +msgid "Could not connect to PrusaLink" +msgstr "Не удалось подключиться к PrusaLink" + +msgid "Note: OctoPrint version at least 1.1.0 is required." +msgstr "Примечание: требуется версия OctoPrint не ниже 1.1.0." + +msgid "Connection refused" +msgstr "Соединение запрещено" + +msgid "Failed to connect to %s port %ld: %s" +msgstr "Не удалось подключиться к порту %s %ld: %s" + +msgid "Couldn't connect to server" +msgstr "Не удалось подключиться к серверу" + +msgid "Couldn't resolve host '%s'" +msgstr "Не удаётся определить имя хоста '%s'" + +msgid "Suggestion" +msgstr "Совет" + +msgid "BambuSource has not correctly been registered for media playing! Press Yes to re-register it." +msgstr "Компонент BambuSource неправильно зарегистрирован для воспроизведения мультимедиа! Нажмите «Да», чтобы перерегистрировать его." + +msgid "Missing BambuSource component registered for media playing! Please re-install BambuStutio or seek after-sales help." +msgstr "Отсутствует компонент BambuSource, зарегистрированный для воспроизведения мультимедиа! Пожалуйста, переустановите BambuStutio или обратитесь в поддержку." + +msgid "Test storage" +msgstr "Тест накопителя" + +msgid "Test BambuLab" +msgstr "Тест BambuLab" + +msgid "Test BambuLab:" +msgstr "Тест BambuLab:" + +msgid "Test Bing.com" +msgstr "Тест Bing.com" + +msgid "Test bing.com:" +msgstr "Тест bing.com:" + +msgid "Test HTTP" +msgstr "Тест HTTP" + +msgid "Test HTTP Service:" +msgstr "Тест HTTP сервера:" + +msgid "Test storage upgrade" +msgstr "Тест накопителя (обновление)" + +msgid "Test Storage Upgrade:" +msgstr "Тест накопителя (обновление):" + +msgid "Test Storage Upload" +msgstr "Тест накопителя (отправка)" + +msgid "Test Storage Upload:" +msgstr "Тест накопителя (отправка):" + +msgid "Test storage download" +msgstr "Тест накопителя (загрузка)" + +msgid "Test Storage Download:" +msgstr "Тест накопителя (загрузка):" + +msgid "Log Info" +msgstr "Журнал сведений" + +msgid "Studio Version:" +msgstr "Версия программы:" + +msgid "System Version:" +msgstr "Версия ОС:" + +msgid "Start Test Multi-Thread" +msgstr "Запуск многопоточного теста" + +msgid "Start Test Single-Thread" +msgstr "Запуск однопоточного теста" + +msgid "Network Test" +msgstr "Проверка сети" + +msgid "Multimaterial" +msgstr "Экструдер ММ" + +msgid "Single extruder multimaterial setup" +msgstr "Мультиматериальный одиночный экструдер" + +msgid "Single Extruder Multi Material" +msgstr "Мультиматериальный одиночный экструдер" + +msgid "Use single nozzle to print multi filament" +msgstr "Использование одной экструзионной головы для печати несколькими видами/цветами пластика." + +msgid "Wipe tower" +msgstr "Черновая башня" + +msgid "Purge in prime tower" +msgstr "Очистка в черновую башню" + +msgid "Purge remaining filament into prime tower" +msgstr "Очистка сопла от остатков материала в черновую башню" + +msgid "Enable filament ramming" +msgstr "Включить рэмминг прутка" + +msgid "No sparse layers (EXPERIMENTAL)" +msgstr "Отсутствие разреженных слоёв (экспериментально)" + +msgid "If enabled, the wipe tower will not be printed on layers with no toolchanges. On layers with a toolchange, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print." +msgstr "Если этот параметр включён, черновая башня не будет печататься на слоях где не происходит смена инструмента. На слоях, где происходит смена инструмента, экструдер будет опускаться вниз до верхней части черновой башни, чтобы напечатать её. Эта функция помечена как экспериментальная, поэтому пользователь несёт ответственность за то, чтобы не допустить столкновения экструдера с напечатанным." + +msgid "Prime all printing extruders" +msgstr "Подготовка всех печатающих экструдеров" + +msgid "If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print." +msgstr "Если этот параметр включён, все печатающие экструдеры в начале печати будут подготавливаться на переднем крае стола." + +msgid "Single extruder multimaterial parameters" +msgstr "Параметры мультиматериального одиночного экструдера" + +msgid "Cooling tube position" +msgstr "Позиция охлаждающей трубки" + +msgid "Distance of the center-point of the cooling tube from the extruder tip." +msgstr "Расстояние между центральной точкой охлаждающей трубки и кончиком экструдера." + +msgid "Cooling tube length" +msgstr "Длина охлаждающей трубки" + +msgid "Length of the cooling tube to limit space for cooling moves inside it." +msgstr "Длина охлаждающей трубки для ограничения перемещения при охлаждающих движениях." + +msgid "Filament parking position" +msgstr "Положение парковки прутка" + +msgid "Distance of the extruder tip from the position where the filament is parked when unloaded. This should match the value in printer firmware." +msgstr "Расстояние от кончика экструдера до точки, где размещается пруток при выгрузке. Расстояние должно соответствовать значению в прошивке принтера." + +msgid "Extra loading distance" +msgstr "Дополнительная длина загрузки" + +msgid "When set to zero, the distance the filament is moved from parking position during load is exactly the same as it was moved back during unload. When positive, it is loaded further, if negative, the loading move is shorter than unloading." +msgstr "Если установлено 0, то расстояние, которое проходит пруток при перемещении из положения парковки во время загрузки, точно такое же, как и при выгрузке. При положительном значении, она загружается дальше; при отрицательном, ход загрузки короче (по сравнению с выгрузкой)." + +msgid "High extruder current on filament swap" +msgstr "Повышение тока экструдера при замене прутка" + +msgid "It may be beneficial to increase the extruder motor current during the filament exchange sequence to allow for rapid ramming feed rates and to overcome resistance when loading a filament with an ugly shaped tip." +msgstr "Это может быть полезно для увеличения тока двигателя экструдера во время замены прутка, чтобы быстро увеличить скорость подачи и преодолеть сопротивление при загрузке прутка с плохой формой кончика." + +msgid "Toolchange parameters with single extruder MM printers" +msgstr "Параметры смены инструмента в одноэкструдерных мультиматериальных принтерах" + +msgid "Toolchange parameters with multi extruder MM printers" +msgstr "Параметры смены инструмента в мультиэкструдерных мультиматериальных принтерах" + +msgid "Ramming settings" +msgstr "Настройки рэмминга" + +msgid "Loading speed" +msgstr "Скорость загрузки" + +msgid "Speed used for loading the filament on the wipe tower." +msgstr "Скорость загрузки прутка при печати черновой башни." + +msgid "Loading speed at the start" +msgstr "Начальная скорость загрузки" + +msgid "Speed used at the very beginning of loading phase." +msgstr "Скорость в начальной фазе загрузки прутка." + +msgid "Unloading speed" +msgstr "Скорость выгрузки" + +msgid "Speed used for unloading the filament on the wipe tower (does not affect initial part of unloading just after ramming)." +msgstr "Скорость выгрузки прутка на черновую башню. (не влияет на начальную фазу выгрузки сразу после рэмминга)." + +msgid "Unloading speed at the start" +msgstr "Начальная скорость выгрузки" + +msgid "Speed used for unloading the tip of the filament immediately after ramming." +msgstr "Скорость выгрузки кончика прутка сразу после рэмминга." + +msgid "Delay after unloading" +msgstr "Задержка после выгрузки" + +msgid "Time to wait after the filament is unloaded. May help to get reliable toolchanges with flexible materials that may need more time to shrink to original dimensions." +msgstr "Время ожидания после выгрузки прутка. Это может помочь вам легко сменить сопло при печати гибкими материалами, которым требуется больше времени, чтобы вернуться к своим первоначальным размерам." + +msgid "Wipe tower parameters" +msgstr "Параметры черновой башни" + +#: src/libslic3r/PrintConfig.cpp:1048 +msgid "Number of cooling moves" +msgstr "Количество охлаждающих движений" + +msgid "Filament is cooled by being moved back and forth in the cooling tubes. Specify desired number of these moves." +msgstr "Пруток охлаждается в охлаждающих трубках путём перемещения назад и вперёд. Укажите желаемое количество таких движений." + +msgid "Speed of the first cooling move" +msgstr "Скорость первого охлаждающего движения" + +msgid "Cooling moves are gradually accelerating beginning at this speed." +msgstr "Охлаждающие движения постепенно ускоряются, начиная с этой скорости." + +msgid "Speed of the last cooling move" +msgstr "Скорость последнего охлаждающего движения" + +msgid "Cooling moves are gradually accelerating towards this speed." +msgstr "Охлаждающие движения постепенно ускоряют до этой скорости." + +msgid "Time for the printer firmware (or the Multi Material Unit 2.0) to load a new filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator." +msgstr "Время за которое прошивка принтера (или Multi Material Unit 2.0) выгружает пруток во время смены инструмента (при выполнении кода Т). Это время добавляется к общему времени печати с помощью алгоритма оценки времени выполнения G-кода." + +msgid "Ramming parameters" +msgstr "Параметры рэмминга" + +msgid "This string is edited by RammingDialog and contains ramming specific parameters." +msgstr "Эта строка редактируется диалоговым окном рэмминга и содержит его конкретные параметры." + +msgid "Time for the printer firmware (or the Multi Material Unit 2.0) to unload a filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator." +msgstr "Время за которое прошивка принтера (или Multi Material Unit 2.0) выгружает пруток во время смены инструмента (при выполнении кода Т). Это время добавляется к общему времени печати с помощью алгоритма оценки времени выполнения G-кода." + +msgid "Enable ramming for multitool setups" +msgstr "Включить рэмминг для мультиинструментальных устройств" + +msgid "Perform ramming when using multitool printer (i.e. when the 'Single Extruder Multimaterial' in Printer Settings is unchecked). When checked, a small amount of filament is rapidly extruded on the wipe tower just before the toolchange. This option is only used when the wipe tower is enabled." +msgstr "Выполнять рэмминг при использовании мультиинструментального принтера (т. е. когда в настройках принтера снят флажок «Мультиматериальный одиночный экструдер»). При включении этой опции, небольшое количество материала быстро выдавливается на черновую башню непосредственно перед сменой инструмента. Эта опция используется только в том случае, если включена черновая башня." + +msgid "Multitool ramming volume" +msgstr "Объём рэмминга мультиинструмента" + +msgid "The volume to be rammed before the toolchange." +msgstr "Объём рэмминга перед сменой инструмента." + +msgid "Multitool ramming flow" +msgstr "Поток рэмминга мультиинструмента" + +msgid "Flow used for ramming the filament before the toolchange." +msgstr "Поток рэмминга пластиковой нити перед сменой инструмента." + +msgid "" +"Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself be reinserted later. This phase is important and different materials can require different extrusion speeds to get the good shape. For this reason, the extrusion rates " +"during ramming are adjustable.\n" +"\n" +"This is an expert-level setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc." +msgstr "" +"Рэмминг (ramming, дословно утрамбовка) означает быстрое экструдирование непосредственно перед сменой инструмента в одноэкструдерном мультиматериальном принтере. Цель процесса состоит в том, чтобы правильно сформировать конец выгружаемого прутка, чтобы он не препятствовал вставке нового прутка или этого же прутка, вставленного позже. Эта фаза важна и разные материалы могут потребовать разных " +"скоростей экструзии, чтобы получить хорошую форму. По этой причине скорость экструзии во время рэмминга регулируется.\n" +"\n" +"Эта опция для опытных пользователей, неправильная настройка может привести к замятию, протиранию прутка приводом экструдера и т.д." + +msgid "Total ramming time" +msgstr "Общее время рэмминга" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:118 +msgid "Total rammed volume" +msgstr "Общий объём при рэмминге" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:122 +msgid "Ramming line width" +msgstr "Ширина линии при рэмминге" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:124 +msgid "Ramming line spacing" +msgstr "Расстояние между линиями при рэмминге" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:175 +msgid "Wipe tower - Purging volume adjustment" +msgstr "Черновая башня - регулировка объёма сброса пластика" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:301 +msgid "Here you can adjust required purging volume (mm³) for any given pair of tools." +msgstr "Здесь вы можете отрегулировать требуемый объём очистки (мм³) для любой пары инструментов." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:302 +msgid "Extruder changed to" +msgstr "Экструдер перешёл на - " + +#: src/slic3r/GUI/WipeTowerDialog.cpp:354 +msgid "Tool #" +msgstr "Инструмент #" + +#: src/slic3r/GUI/WipeTowerDialog.cpp:363 +msgid "Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded." +msgstr "Общий объём прочистки вычисляется путём суммирования двух нижеуказанных значений, в зависимости от того, какие инструменты предзагружены/выгружены." + +#: src/slic3r/GUI/WipeTowerDialog.cpp:364 +msgid "Volume to purge (mm³) when the filament is being" +msgstr "Объём прочистки (мм³) при выдавливании прутка" + +msgid "Volumetric speed" +msgstr "Объёмная скорость потока" + +msgid "Tip Diameter" +msgstr "Диаметр кончика ветки" + +#. TRN PrintSettings: "Organic supports" > "Tip Diameter" +msgid "Branch tip diameter for organic supports." +msgstr "Диаметр кончика ветки органической поддержки." + +#: src/libslic3r/PrintConfig.cpp:2952 +msgid "Branch Diameter" +msgstr "Диаметр ветвей" + +#. TRN PrintSettings: "Organic supports" > "Branch Diameter" +msgid "The diameter of the thinnest branches of organic support. Thicker branches are more sturdy. Branches towards the base will be thicker than this." +msgstr "Диаметр самых тонких ветвей органической поддержки. Чем толще ветви, тем они крепче. Ветви, идущие к основанию, будут утолщаться." + +msgid "Branch Diameter Angle" +msgstr "Угол изменения диаметра ветвей" + +#. TRN PrintSettings: "Organic supports" > "Branch Diameter Angle" +msgid "The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the organic support." +msgstr "Угол изменения диаметра ветвей по мере их постепенного утолщения к основанию. Если значение угла равно 0, ветви будут иметь одинаковую толщину по всей своей длине. Небольшой угол может повысить устойчивость органической поддержки." + +#: src/libslic3r/PrintConfig.cpp:2978 +msgid "Branch Diameter with double walls" +msgstr "Диаметр ветвей с двойными стенками" + +#. TRN PrintSettings: "Organic supports" > "Branch Diameter" +msgid "Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. Set this value to zero for no double walls." +msgstr "Ветви, толщина которых больше указанного диаметра, будут напечатаны с двойными стенками для прочности. Установите 0, если двойные стенки у ветвей не нужны." + +msgid "Branch Distance" +msgstr "Расстояние между ветками" + +#. TRN PrintSettings: "Organic supports" > "Branch Distance" +msgid "How far apart the branches need to be when they touch the model. Making this distance small will cause the tree support to touch the model at more points, causing better overhang but making support harder to remove." +msgstr "Указывает, насколько далеко друг от друга должны располагаться ветви при касании модели. Если задать небольшое расстояние, то увеличится количество точек, в которых древовидная поддержка касается модели. Это улучшит печать нависаний, но при этом усложнит удаление поддержки." + +#: src/libslic3r/PrintConfig.cpp:3004 +msgid "Branch Density" +msgstr "Плотность ветвей" + +#. TRN PrintSettings: "Organic supports" > "Branch Density" +msgid "Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed." +msgstr "Регулирует плотность создания ветвей в месте контакта с моделью. Большее значение приводит к улучшению качества печати нависаний, но такие поддержки сложнее удалять, поэтому рекомендуется вместо высокого значения плотности ветвей включать связующие слои поддержки." + +# +++++++++++++++++++++ +msgid "Vertical" +msgstr "Вертикальная линия" + +msgid "Horizontal" +msgstr "Горизонтальная линия" + +msgid "Choose one or more files (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" +msgstr "Выберите один или несколько файлов (3mf/step/stl/svg/obj/amf/usd*/abc/ply):" + +msgid "/" +msgstr "/" + +msgid "The printer does not currently support auto refill." +msgstr "В настоящее время принтер не поддерживает функцию автодозаправки." + +msgid "AMS filament backup is not enabled, please enable it in the AMS settings." +msgstr "Резервирование материала АСПП не включено, пожалуйста, включите его в настройках АСПП." + +msgid "" +"If there are two identical filaments in AMS, AMS filament backup will be enabled. \n" +"(Currently supporting automatic supply of consumables with the same brand, material type, and color)" +msgstr "" +"При наличии в АСПП двух одинаковых материалов включается функция резервирования материала (автодозаправка). \n" +"(В настоящее время поддерживается автоматическая дозаправка материала только одного производителя, типа и цвета)" + +#, c-format, boost-format +msgid "Current chamber temperature is higher than the material's safe temperature,it may result in material softening and clogging.The maximum safe temperature for the material is %d" +msgstr "Текущая температура в камере превышает безопасную температуру для этого материала, что может привести к размягчению материала или засорению экструдера. Безопасная температура текущего материала составляет %d." + +msgid " But machines with I3 structure will not generate timelapse videos." +msgstr " Но принтеры с кинематикой I3 не будут писать таймлапс." + +msgid "Filament unloading" +msgstr "Выгрузка прутка" + +# ??? Пауза при пропуске шагов +msgid "Skip step pause" +msgstr "Пропуск команды паузы" + +msgid "Filament loading" +msgstr "Загрузка прутка" + +msgid "Motor noise calibration" +msgstr "Калибровка шума двигателя" + +msgid "Paused due to AMS lost" +msgstr "Печать приостановлена из-за потери связи с АСПП" + +msgid "Paused due to low speed of the heat break fan" +msgstr "Печать приостановлена из-за низкой скорости вращения вентилятора головы" + +msgid "Paused due to chamber temperature control error" +msgstr "Печать приостановлена из-за ошибки контроля температуры в камере" + +# ??? или Снижение температуры +msgid "Cooling chamber" +msgstr "Охлаждение камеры" + +msgid "Paused by the Gcode inserted by user" +msgstr "Печать приостановлена G-кодом, вставленным пользователем" + +# Демонстрация шума двигателя +msgid "Motor noise showoff" +msgstr "Результат калибровки шума двигателя" + +msgid "The current chamber temperature or the target chamber temperature exceeds 45℃.In order to avoid extruder clogging,low temperature filament(PLA/PETG/TPU) is not allowed to be loaded." +msgstr "Текущая температура в камере или целевая температура в камере превышает 45℃. Чтобы избежать засорения экструдера, запрещается загрузка низкотемпературной печатной нити (PLA/PETG/TPU)." + +msgid "Low temperature filament(PLA/PETG/TPU) is loaded in the extruder.In order to avoid extruder clogging,it is not allowed to set the chamber temperature above 45℃." +msgstr "В экструдер загружается низкотемпературная пластиковая нить (PLA/PETG/TPU). Чтобы избежать засорения экструдера, запрещается устанавливать температуру в камере выше 45℃." + +msgid "When you set the chamber temperature below 40℃, the chamber temperature control will not be activated. And the target chamber temperature will automatically be set to 0℃." +msgstr "Если вы установили температура в камере ниже 40℃, то контроль температуры в камере не запустится, а целевая температура в ней будет автоматически установлена на 0℃." + +msgid "This calibration does not support the currently selected nozzle diameter" +msgstr "Данная калибровка не поддерживает выбранный диаметр сопла" + +msgid "Current flowrate cali param is invalid" +msgstr "Текущая величина калибровки скорости потока недопустима" + +msgid "Selected diameter and machine diameter do not match" +msgstr "Выбранный диаметр и диаметр профиля принтера не совпадают" + +msgid "Failed to generate cali gcode" +msgstr "Не удалось сгенерировать калибровочный G-код" + +msgid "Align to Y axis" +msgstr "Выравнивать по оси Y" + +msgid "Vibration compensation" +msgstr "Компенсация вибрации" + +msgid "Motor noise cancellation" +msgstr "Шумоподавление двигателя" + +msgid "No step selected" +msgstr "Шаг не задан" + +msgid "" +"Another virtual camera is running.\n" +"Orca Slicer supports only a single virtual camera.\n" +"Do you want to stop this virtual camera?" +msgstr "" +"Уже работает одна виртуальная камера.\n" +"Orca Slicer поддерживает только одну виртуальную камеру.\n" +"Хотите остановить эту виртуальную камеру?" + +msgid "The .gcode.3mf file contains no G-code data.Please slice it with Orca Slicer and export a new .gcode.3mf file." +msgstr "Файл .gcode.3mf не содержит G-кода. Пожалуйста, нарежьте его в программе Orca Slicer и экспортируйте новый файл .gcode.3mf." + +msgid "" +"You have completed printing the mall model, \n" +"but the synchronization of rating information has failed." +msgstr "" +"Вы завершили печать модели торгового центра, \n" +"но не удалось синхронизировать информацию о рейтинге." + +msgid "How do you like this printing file?" +msgstr "На сколько вы оцениваете этот напечатанный файл?" + +msgid "(The model has already been rated. Your rating will overwrite the previous rating.)" +msgstr "(Модели уже присвоен рейтинг. Ваш рейтинг перезапишет предыдущий.)" + +msgid "Rate" +msgstr "Оценка" + +msgid "Rate the Print Profile" +msgstr "Оценить профиль печати" + +msgid "Comment" +msgstr "Комментарий" + +msgid "Rate this print" +msgstr "Оценить эту печать" + +msgid "Add Photo" +msgstr "Добавить фото" + +msgid "Delete Photo" +msgstr "Удалить фото" + +msgid "Submit" +msgstr "Отправить" + +msgid "Please click on the star first." +msgstr "Пожалуйста, сначала нажмите на звездочку." + +msgid "InFo" +msgstr "Информация" + +msgid "Get oss config failed." +msgstr "" +"Не удалось получить конфигурацию OSS.\n" +"\n" +"Ошибка получения конфигурации OSS." + +msgid "Upload Pictrues" +msgstr "Отправка изображений" + +msgid "Number of images successfully uploaded" +msgstr "Количество успешно загруженных изображений" + +msgid " upload failed" +msgstr " ошибка отправки" + +msgid " upload config prase failed\n" +msgstr " ошибка обработки конфигурации при отправке\n" + +# ??? +msgid " No corresponding storage bucket\n" +msgstr " Отсутствует хранилище данных\n" + +msgid " can not be opened\n" +msgstr " не удаётся открыть\n" + +msgid "" +"The following issues occurred during the process of uploading images. Do you want to ignore them?\n" +"\n" +msgstr "" +"В процессе загрузки изображений возникли следующие проблемы. Игнорировать их?\n" +"\n" + +msgid "Synchronizing the printing results. Please retry a few seconds later." +msgstr "Синхронизация результатов печати. Повторите попытку через несколько секунд." + +msgid "Upload failed\n" +msgstr "Ошибка отправки\n" + +msgid "obtaining instance_id failed\n" +msgstr "не удалось получить instance_id\n" + +msgid "" +"Your comment result cannot be uploaded due to some reasons. As follows:\n" +"\n" +" error code: " +msgstr "" +"Ваш комментарий не может быть отправлен по некоторым причинам. Причины:\n" +"\n" +"Код ошибки: " + +msgid "error message: " +msgstr "сообщение об ошибке: " + +msgid "" +"\n" +"\n" +"Would you like to redirect to the webpage for rating?" +msgstr "" +"\n" +"\n" +"Хотите перейти на страницу для выставления оценки?" + +msgid "Some of your images failed to upload. Would you like to redirect to the webpage for rating?" +msgstr "Некоторые из ваших изображений не удалось загрузить. Хотите перейти на страницу для выставления оценки?" + +msgid "You can select up to 16 images." +msgstr "Допускается выбор до 16 изображений." + +msgid "" +"At least one successful print record of this print profile is required \n" +"to give a positive rating(4 or 5stars)." +msgstr "Для выставления положительной оценки (4 или 5 звезд) требуется хотя бы одна успешная запись о печати данным профилем печати." + +msgid "Model file downloaded." +msgstr "Файл модели скачан." + +msgid "Allow Prompt Sound" +msgstr "Разрешить звуковые уведомления" + +msgid "Remove current plate (if not last one)" +msgstr "Удалить текущую печатную пластину (кроме последней)" + +msgid "Auto orient objects on current plate" +msgstr "Автоориентация моделей на текущей печатной пластине" + +msgid "Arrange objects on current plate" +msgstr "Расставить модели на текущей печатной пластине" + +msgid "Unlock current plate" +msgstr "Разблокировать текущую печатную пластину" + +msgid "Lock current plate" +msgstr "Заблокировать текущую печатную пластину" + +msgid "Customize current plate" +msgstr "Настроить текущую печатную пластину" + +msgid "The current hot bed temperature is relatively high. The nozzle may be clogged when printing this filament in a closed enclosure. Please open the front door and/or remove the upper glass." +msgstr "Текущая температура стола довольно высока. При печати этим материалом в закрытом корпусе возможно засорение сопла. Откройте переднюю дверцу и/или верхнюю крышку принтера." + +msgid "Enabling traditional timelapse photography may cause surface imperfections. It is recommended to change to smooth mode." +msgstr "Включение обычного режима таймлапса может привести к появлению дефектов поверхности, поэтому рекомендуется изменить режим на плавный." + +msgid "Replace from:" +msgstr "Заменить из:" + +msgid "Unable to replace with more than one volume" +msgstr "Невозможно заменить более чем одним объём" + +msgid "Do you want to replace it" +msgstr "Хотите заменить его" + +msgid "Reload from:" +msgstr "Перезагрузка из:" + +msgid "Unable to reload:" +msgstr "Не удалось перезагрузить:" + +msgid "Error during reload" +msgstr "Ошибка во время перезагрузки" + +msgid "Importing to Orca Slicer failed. Please download the file and manually import it." +msgstr "Не удалось импортировать в Orca Slicer. Загрузите файл и импортируйте его вручную." + +msgid "Save SLA file as:" +msgstr "Сохранить SLA файл как:" + +msgid "The provided file name is not valid." +msgstr "Указано недопустимое имя файла." + +msgid "The following characters are not allowed by a FAT file system:" +msgstr "Следующие символы не разрешены файловой системой FAT:" + +msgid "Use free camera" +msgstr "Использовать свободную камеру" + +msgid "If enabled, use free camera. If not enabled, use constrained camera." +msgstr "Если включено, используется свободное вращение камеры. Если выключено, используется вращение камера с ограничениями." + +msgid "Bambu Engineering Plate" +msgstr "Инженерная пластина Bambu" + +msgid "Bambu Smooth PEI Plate" +msgstr "Гладкая PEI пластина Bambu" + +msgid "High temperature Plate" +msgstr "Высокотемпературная пластина" + +msgid "Bambu Textured PEI Plate" +msgstr "Текстурированная PEI пластина Bambu" + +msgid "Click here if you can't connect to the printer" +msgstr "Не удаётся подключиться к принтеру?" + +msgid "When enable spiral vase mode, machines with I3 structure will not generate timelapse videos." +msgstr "При включении режима «Спиральная ваза» принтеры с кинематикой I3 не будут писать таймлапс." + +msgid "When print by object, machines with I3 structure will not generate timelapse videos." +msgstr "При печати по очереди, принтеры с кинематикой I3 не будут писать таймлапс." + +msgid "The printer is required to be in the same LAN as Orca Slicer." +msgstr "Принтер должен находиться в одной локальной сети с Orca Slicer." + +msgid "Print chamber temperature" +msgstr "Температура в термокамере" + +msgid "Smooth PEI Plate / High Temp Plate" +msgstr "Гладкая PEI/высокотемпер. пластина" + +msgid "Bed temperature when Smooth PEI Plate/High temperature plate is installed. Value 0 means the filament does not support to print on the Smooth PEI Plate/High Temp Plate" +msgstr "Температура стола при установленной гладкой PEI/высокотемпературный печатной пластине. 0 означает, что пластиковая нить не поддерживает печать на этой печатной пластине." + +msgid "Exhaust fan" +msgstr "Вытяжной вентилятор" + +msgid "During print" +msgstr "Во время печати" + +msgid "Complete print" +msgstr "После завершения печати" + +msgid "Time lapse G-code" +msgstr "G-код таймлапса" + +msgid "Click OK to update the Network plug-in when Orca Slicer launches next time." +msgstr "Нажмите OK, чтобы обновить сетевой плагин при следующем запуске Orca Slicer." + +msgid "New version of Orca Slicer" +msgstr "Доступна новая версия Orca Slicer" + +msgid "Step 1, please confirm Orca Slicer and your printer are in the same LAN." +msgstr "Шаг 1. Пожалуйста, убедитесь, что Orca Slicer и ваш принтер находятся в одной локальной сети." + +msgid "Invalid spacing supplied to Flow::with_spacing(), check your layer height and extrusion width" +msgstr "Для Flow::with_spacing () был указан недопустимый интервал. Проверьте высоту слоя и ширину экструзии." + +#, boost-format +msgid "The object %1% exceeds the maximum build volume height." +msgstr "Высота модели %1% превышает максимально допустимую." + +#, boost-format +msgid "While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height." +msgstr "Хотя сама модель %1% вписывается в область построения, её последний слой превышает максимальную высоту области построения." + +msgid "You might want to reduce the size of your model or change current print settings and retry." +msgstr "Попробуйте уменьшить размер модели или изменить текущие настройки печати и повторить попытку." + +msgid "Variable layer height is not supported with Organic supports." +msgstr "Функция переменной высоты слоя не совместима органическими поддержками." + +msgid "Elephant foot compensation layers" +msgstr "Компенсирующих слоёв «слоновьей ноги»" + +msgid "The number of layers on which the elephant foot compensation will be active. The first layer will be shrunk by the elephant foot compensation value, then the next layers will be linearly shrunk less, up to the layer indicated by this value." +msgstr "Количество слоёв, на которые будет распространяться компенсация слоновьей ноги. Первый слой будет уменьшен на величину компенсации слоновьей ноги с последующим линейным уменьшением до слоя, указанного здесь." + +# от Overhang reversal +# Реверс на нечётных слоях +msgid "Reverse on odd" +msgstr "Реверс на нависаниях" + +msgid "Overhang reversal" +msgstr "Реверс на нависаниях" + +msgid "Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhang." +msgstr "Печать периметров, имеющих нависания, в обратном направлении на нечётных слоях. Такое чередование может значительно улучшить качество печати крутых нависаний." + +# от Overhang reversal threshold +msgid "Reverse threshold" +msgstr "Порог для реверса" + +msgid "Overhang reversal threshold" +msgstr "Порог разворота на свесах" + +msgid "" +"Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width.\n" +"Value 0 enables reversal on every odd layers regardless." +msgstr "" +"Величина свеса периметра при которой она считается достаточной для активации функции реверса печати нависаний.\n" +"Может быть задано как в процентах, так и в миллиметрах от ширины периметра." + +# ??? Замедление печати на изогнутых периметров +msgid "Slow down for curled perimeters" +msgstr "Снижение скорости на изогнутых периметрах" + +msgid "Enable this option to slow printing down in areas where potential curled perimeters may exist" +msgstr "Включите эту опцию для замедления печати в тех областях, где потенциально могут возникать изогнутые периметры." + +msgid "Activate air filtration" +msgstr "Вкл. фильтрацию воздуха" + +msgid "Activate for better air filtration. G-code command: M106 P3 S(0-255)" +msgstr "Активировать для лучшей фильтрации воздуха. G-код команда: M106 P3 S(0-255)" + +msgid "Speed of exhuast fan during printing.This speed will overwrite the speed in filament custom gcode" +msgstr "Скорость вытяжного вентилятора во время печати. Эта скорость будет переопределять скорость в пользовательском стартовом G-коде материала" + +msgid "Speed of exhuast fan after printing completes" +msgstr "Скорость вытяжного вентилятора после завершения печати" + +msgid "Softening temperature" +msgstr "Температура размягчения" + +msgid "The material softens at this temperature, so when the bed temperature is equal to or greater than it, it's highly recommended to open the front door and/or remove the upper glass to avoid cloggings." +msgstr "При этой температуре материал размягчается, поэтому, когда температура стола равна или превышает её, настоятельно рекомендуется открыть переднюю дверцу и/или верхнюю крышку принтера, чтобы избежать засорения сопла." + +msgid "Acceleration of initial layer. Using a lower value can improve build plate adhesive" +msgstr "Ускорение на первом слое. Использование более низкого значения может улучшить адгезию к столу." + +#, c-format, boost-format +msgid "Klipper's max_accel_to_decel will be adjusted to this %% of acceleration" +msgstr "Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) будет скорректировано на данное ускорение: %%\"" + +#, c-format, boost-format +msgid "%%" +msgstr "%%" + +msgid "Filter out gaps smaller than the threshold specified" +msgstr "Небольшие промежутки меньше указанного порога не будут заполняться." + +msgid "Printer structure" +msgstr "Кинематика принтера" + +msgid "The physical arrangement and components of a printing device" +msgstr "Конструкция физического принтера" + +msgid "CoreXY" +msgstr "CoreXY" + +msgid "I3" +msgstr "I3" + +msgid "Hbot" +msgstr "Hbot" + +msgid "Delta" +msgstr "Delta" + +msgid "Best object position" +msgstr "Наилучшее расположение модели" + +msgid "Best auto arranging position in range [0,1] w.r.t. bed shape." +msgstr "Наилучшее расположение модели при авторасстановке в диапазоне [0,1] относительно формы стола." + +msgid "Enable this option if machine has auxiliary part cooling fan. G-code command: M106 P2 S(0-255)." +msgstr "" +"Если в принтере имеет вспомогательный вентилятор для охлаждения моделей, можете включить эту опцию. \n" +"G-код команда: M106 P2 S(0-255)." + +msgid "Time cost" +msgstr "Цена печати" + +msgid "The printer cost per hour" +msgstr "Стоимость печати в час." + +msgid "money/h" +msgstr "цена/ч" + +msgid "Support control chamber temperature" +msgstr "Контроль температуры в термокамере" + +msgid "" +"This option is enabled if machine support controlling chamber temperature\n" +"G-code command: M141 S(0-255)" +msgstr "" +"Если принтер поддерживает контроль температуры в камере, включите эту опцию.\n" +"G-код команда: M141 S(0-255)" + +msgid "Support air filtration" +msgstr "Фильтрация выдуваемого воздуха" + +msgid "" +"Enable this if printer support air filtration\n" +"G-code command: M106 P3 S(0-255)" +msgstr "" +"Если принтер поддерживает фильтрацию выдуваемого воздуха, включите эту опцию. \n" +"G-код команда: M106 P3 S(0-255)" + +msgid "The pattern that will be used when ironing" +msgstr "Шаблон по которому будет производиться разглаживание." + +msgid "Ironing angle" +msgstr "Угол разглаживания" + +msgid "The angle ironing is done at. A negative number disables this function and uses the default method." +msgstr "Выбор угла разглаживания. Отрицательное число отключает эту функцию и использует метод по умолчанию." + +# ??? +msgid "Extrusion rate smoothing" +msgstr "Сглаживание скорости экструзии" + +msgid "" +"This parameter smooths out sudden extrusion rate changes that happen when the printer transitions from printing a high flow (high speed/larger width) extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.\n" +"\n" +"It defines the maximum rate by which the extruded volumetric flow in mm3/sec can change over time. Higher values mean higher extrusion rate changes are allowed, resulting in faster speed transitions.\n" +"\n" +"A value of 0 disables the feature. \n" +"\n" +"For a high speed, high flow direct drive printer (like the Bambu lab or Voron) this value is usually not needed. However it can provide some marginal benefit in certain cases where feature speeds vary greatly. For example, when there are aggressive slowdowns due to overhangs. In these cases a high value of around 300-350mm3/s2 is recommended as this allows for just enough smoothing to assist " +"pressure advance achieve a smoother flow transition.\n" +"\n" +"For slower printers without pressure advance, the value should be set much lower. A value of 10-15mm3/s2 is a good starting point for direct drive extruders and 5-10mm3/s2 for Bowden style. \n" +"\n" +"This feature is known as Pressure Equalizer in Prusa slicer.\n" +"\n" +"Note: this parameter disables arc fitting." +msgstr "" +"Этот параметр сглаживает резкие изменения скорости экструзии, которые происходят, когда принтер переходит от печати с большим расходом (высокая скорость/большая ширина) к печати с меньшим расходом (меньшая скорость/меньшая ширина) и наоборот.\n" +"\n" +"Параметр задаёт максимальную скорость, с которой объёмный расход экструдируемого материала может изменяться с течением времени (мм³/с). Более высокие значения означают, что допускаются более высокие изменения скорости экструзии, что приводит к более быстрому переключению скоростей.\n" +"\n" +"Значение 0 отключает эту функцию. \n" +"\n" +"Для высокоскоростных принтеров с прямым приводом (например, Bambu lab или Voron) обычно не требуется использование данного значения. Однако в некоторых случаях, когда скорость печати сильно различается, это может принести некоторую дополнительную пользу. Например, когда происходят резкие замедления из-за нависаний. В этих случаях рекомендуется использовать высокое значение, составляющее около " +"300-350 мм³/с², так как это обеспечивает достаточное сглаживание, помогающее прогнозированию давления достичь более плавного перехода потока.\n" +"\n" +"Для более медленных принтеров, не использующих прогнозирование давления (pressure advance), это значение должно быть значительно ниже. Значение 10-15 мм³/с² является хорошей отправной точкой для экструдеров с прямым приводом и 5-10 мм³/с² для боуден экструдеров.\n" +"\n" +"В Prusa Slicer эта функция известна как «Выравнивание давления» (Pressure equalizer).\n" +"\n" +"Примечание: этот параметр отключает поддержку движения по дуге окружности." + +msgid "mm³/s²" +msgstr "мм³/с²" + +msgid "Smoothing segment length" +msgstr "Длина сглаживающего сегмента" + +msgid "" +"A lower value results in smoother extrusion rate transitions. However, this results in a significantly larger gcode file and more instructions for the printer to process. \n" +"\n" +"Default value of 3 works well for most cases. If your printer is stuttering, increase this value to reduce the number of adjustments made\n" +"\n" +"Allowed values: 1-5" +msgstr "" +"Меньшее значение приводит к более плавному изменению скорости экструзии. Однако это приводит к значительному увеличению размера G-код файла и увеличению количества инструкций для обработки принтером. \n" +"\n" +"Значение по умолчанию, равное 3, хорошо подходит для большинства случаев. Если принтер печатает с мини-фризами, увеличьте это значение, чтобы уменьшить количество выполняемых изменений.\n" +"\n" +"Допустимые значения: 1–5." + +msgid "" +"Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed during printing except the first several layers which is defined by no cooling layers.\n" +"Please enable auxiliary_fan in printer settings to use this feature. G-code command: M106 P2 S(0-255)" +msgstr "" +"Скорость вращения вспомогательного вентилятора для охлаждения моделей. Он всегда будет работать с этой скоростью, за исключением первых нескольких слоёв, которые обычно настроены на работу без охлаждения.\n" +"Пожалуйста, включите вспомогательный вентилятор для охлаждения моделей (auxiliary_fan) в настройках принтера, чтобы использовать эту функцию. \n" +"G-код команда: M106 P2 S(0-255)." + +msgid "The minimum printing speed for the filament when slow down for better layer cooling is enabled, when printing overhangs and when feature speeds are not specified explicitly." +msgstr "Минимальная скорость печати для текущего прутка при включенной функции «Замедлять печать для лучшего охлаждения слоёв» при печати нависаний и когда скорости элементов явно не задана." + +msgid "Z hop lower boundary" +msgstr "Приподнимать ось Z только ниже" + +msgid "Z hop will only come into effect when Z is above this value and is below the parameter: \"Z hop upper boundary\"" +msgstr "Если указать положительное значение, ось Z будет подниматься только ниже (до) заданной здесь высоты (высота считается от стола). Таким образом вы можете запретить подъём оси Z выше установленной высоты." + +msgid "Z hop upper boundary" +msgstr "Приподнимать ось Z только выше" + +msgid "If this value is positive, Z hop will only come into effect when Z is above the parameter: \"Z hop lower boundary\" and is below this value" +msgstr "Если указать положительное значение, ось Z будет подниматься только выше (после) заданной здесь высоты (высота считается от стола). Таким образом вы можете отключить подъём оси Z при печати на первых слоях (в начале печати)." + +msgid "Manual Filament Change" +msgstr "Ручная замена прутка" + +msgid "Enable this option to omit the custom Change filament G-code only at the beginning of the print. The tool change command (e.g., T0) will be skipped throughout the entire print. This is useful for manual multi-material printing, where we use M600/PAUSE to trigger the manual filament change action." +msgstr "Включите эту опцию, если хотите пропустить пользовательский G-код смены прутка только в начале печати. Команда смены инструмента (например, T0) будет пропускаться на протяжении всей печати. Это полезно при ручной мультиматериальной печати, где для запуска операции ручной смены прутка используется команда M600." + +msgid "Z offset" +msgstr "Смещение координат оси Z" + +msgid "This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)." +msgstr "Это значение будет прибавлено (или вычтено) из всех Z координат в выходном G-коде. Это, например, используется для компенсации неправильного положения концевика оси Z." + +msgid "" +"Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.\n" +"For tree support, slim and organic style will merge branches more aggressively and save a lot of material (default organic), while hybrid style will create similar structure to normal support under large flat overhangs." +msgstr "" +"Стиль и форма создаваемой поддержки.\n" +"\n" +"Стиль «Сетка» создаёт более устойчивые опоры (по умолчанию). Стиль «Аккуратный» экономит материал и уменьшает образование дефектов на моделях.\n" +"\n" +"Для древовидной поддержки, при стройном и органическом стиле происходит более агрессивное объединение ветвей и экономия материала (по умолчанию органический). В то время как гибридный стиль создаёт структуру, схожую с обычную поддержкой при больших плоских нависаниях." + +msgid "Activate temperature control" +msgstr "Вкл. контроль температуры" + +msgid "" +"Enable this option for chamber temperature control. An M191 command will be added before \"machine_start_gcode\"\n" +"G-code commands: M141/M191 S(0-255)" +msgstr "" +"Для контроля температуры в камере принтера включите эту опцию. Команда M191 будет добавлена перед стартовый G-кодом принтера (machine_start_gcode).\n" +"G-код команда: M141/M191 S(0-255)" + +msgid "" +"Higher chamber temperature can help suppress or reduce warping and potentially lead to higher interlayer bonding strength for high temperature materials like ABS, ASA, PC, PA and so on.At the same time, the air filtration of ABS and ASA will get worse.While for PLA, PETG, TPU, PVA and other low temperature materials,the actual chamber temperature should not be high to avoid cloggings, so 0 " +"which stands for turning off is highly recommended" +msgstr "" +"Более высокая температура в камере может помочь уменьшить или даже исключить коробление материала. Так же это улучшает межслойное соединения у высокотемпературных материалов, таких как ABS, ASA, PC, PA и т.д. (в то же время фильтрация воздуха при печати ABS и ASA сделает её хуже). Для низкотемпературных материалов, таких как PLA, PETG, TPU, PVA и т. д., фактическая температура в камере не " +"должна быть слишком высокой, чтобы избежать засорения сопла, поэтому настоятельно рекомендуется установить температуру в камере равной 0°C." + +# ??? +# Преобразование отверстий в многограннники +msgid "Convert holes to polyholes" +msgstr "Многогранные отверстия" + +# ??? +# Поиск отверстий близких к кругу в двух или более слоях +# Расчёт многогранного отверстия вычисляется по диаметру сопла. +msgid "" +"Search for almost-circular holes that span more than one layer and convert the geometry to polyholes. Use the nozzle size and the (biggest) diameter to compute the polyhole.\n" +"See http://hydraraptor.blogspot.com/2011/02/polyholes.html" +msgstr "" +"Поиск цилиндрических отверстий в двух или более слоях и преобразование их геометрии в многогранники. Для расчёта многогранного отверстия используется размер сопла и наибольший диаметр найденного отверстия.\n" +"Подробнее на http://hydraraptor.blogspot.com/2011/02/polyholes.html" + +# ??? +# Предел обнаружения многогранного отверстия +msgid "Polyhole detection margin" +msgstr "Предел обнаружения" + +msgid "" +"Maximum defection of a point to the estimated radius of the circle.\n" +"As cylinders are often exported as triangles of varying size, points may not be on the circle circumference. This setting allows you some leway to broaden the detection.\n" +"In mm or in % of the radius." +msgstr "" +"Максимальное отклонение точки от расчётного радиуса окружности.\n" +"Поскольку цилиндры часто экспортируются в виде треугольников разного размера, точки могут находиться не на окружности круга. Эта настройка позволяет в некоторой степени расширить эту область обнаружения.\n" +"Значение задаётся в мм или в процентах от радиуса." + +msgid "Polyhole twist" +msgstr "Скручивание многогранника" + +msgid "Rotate the polyhole every layer." +msgstr "Вращение многогранного отверстия на каждом слое." + +msgid "Format of G-code thumbnails" +msgstr "Формат эскизов G-кода" + +msgid "Format of G-code thumbnails: PNG for best quality, JPG for smallest size, QOI for low memory firmware" +msgstr "Формат эскизов G-кода: PNG для наилучшего качества, JPG для наименьшего размера, QOI для прошивки с малым объемом памяти." + +msgid "Minimum save" +msgstr "Минимальное сохранение" + +msgid "export 3mf with minimum size." +msgstr "экспорт 3mf файла с минимальным размером." + +msgid "Ensure on bed" +msgstr "Обеспечивать размещение на столе" + +msgid "Lift the object above the bed when it is partially below. Disabled by default" +msgstr "Поднимает модель над столом, когда она частично находится ниже его уровня. По умолчанию отключено." + +msgid "Orient Options" +msgstr "Параметры ориентации" + +msgid "Orient options: 0-disable, 1-enable, others-auto" +msgstr "Параметры ориентации: 0 - отключить, 1 - включить, другие - автоматически" + +msgid "Rotation angle around the Z axis in degrees." +msgstr "Угол поворота вокруг оси Z в градусах." + +msgid "Rotate around X" +msgstr "Поворот вокруг оси X" + +msgid "Rotation angle around the X axis in degrees." +msgstr "Угол поворота вокруг оси X в градусах." + +msgid "Rotate around Y" +msgstr "Поворот вокруг оси Y" + +msgid "Rotation angle around the Y axis in degrees." +msgstr "Угол поворота вокруг оси Y в градусах." + +msgid "Load custom gcode" +msgstr "Загрузить пользовательский G-код" + +msgid "Load custom gcode from json" +msgstr "Загрузить пользовательской G-код из json" + +#, c-format, boost-format +msgid "" +"Please input valid values:\n" +"Start value: >= %.1f\n" +"End value: <= %.1f\n" +"End value: > Start value\n" +"Value step: >= %.3f)" +msgstr "" +"Введите допустимое значение:\n" +"Начальное: >= %.1f\n" +"Конечное: <= %.1f\n" +"Конечное: > Начальное\n" +"Шаг: >= %.3f)" + +msgid "Please find the cornor with perfect degree of extrusion" +msgstr "Пожалуйста, найдите угол с идеальной степенью экструзии" + +msgid "Pattern" +msgstr "Шаблон" + +msgid "From k Value" +msgstr "Начальное значение коэф. K" + +msgid "To k Value" +msgstr "Конечное значение коэф. K" + +msgid "Step value" +msgstr "Шаг" + +msgid "0.5" +msgstr "0.5" + +msgid "0.005" +msgstr "0.005" + +msgid "" +"Please input valid values:\n" +"start > 0 \n" +"step >= 0\n" +"end > start + step)" +msgstr "" +"Введите допустимое значение:\n" +"Начальное > 0\n" +"Шаг >= 0\n" +"Конечное > Начальное + Шаг" + +msgid "" +"Please input valid values:\n" +"start > 10 \n" +"step >= 0\n" +"end > start + step)" +msgstr "" +"Введите допустимое значение:\n" +"Начальное > 10\n" +"Шаг >= 0\n" +"Конечное > Начальное + Шаг" + +msgid "The start, end or step is not valid value." +msgstr "Недопустимое значение: начальное, конечное или шаг." + +msgid "Unable to calibrate: maybe because the set calibration value range is too large, or the step is too small" +msgstr "Невозможно выполнить калибровку: возможно, установленный диапазон значений калибровки слишком велик или шаг слишком мал." + +msgid "Need select printer" +msgstr "Нужно выбрать принтер" + +#: resources/data/hints.ini: [hint:When need to print with the printer door opened] msgid "" "When need to print with the printer door opened\n" -"Opening the printer door can reduce the probability of extruder/hotend " -"clogging when printing lower temperature filament with a higher enclosure " -"temperature. More info about this in the Wiki." +"Opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature. More info about this in the Wiki." msgstr "" "Когда необходимо печатать с открытой дверцей принтера?\n" -"При печати низкотемпературным материалом при более высокой температуре в " -"камере, открытие дверцы принтера снижает вероятность засорения экструдера/" -"хотэнда. Более подробную информацию читайте на вики-сайте." +"При печати низкотемпературным материалом при более высокой температуре в камере, открытие дверцы принтера снижает вероятность засорения экструдера/хотэнда. Более подробную информацию читайте на вики-сайте." -#~ msgid "Embeded" -#~ msgstr "Проникновение" +# ++++++++++++++++++++++++++++ beta2 +msgid "Total Estimation" +msgstr "Общая оценка" -#~ msgid "" -#~ "OrcaSlicer configuration file may be corrupted and is not abled to be " -#~ "parsed.Please delete the file and try again." -#~ msgstr "" -#~ "Возможно, файл конфигурации OrcaSlicer повреждён и не может быть " -#~ "обработан. Пожалуйста, удалите файл и повторите попытку." +msgid "Home" +msgstr "Домашняя страница" -#~ msgid "AMS %s" -#~ msgstr "АСПП №%s" +msgid "Default Page" +msgstr "" +"Страница \n" +"по умолчанию" -#~ msgid "Cali" -#~ msgstr "Калиб." +msgid "Set the page opened on startup." +msgstr "Задание страницы, открываемой при запуске приложения." -#~ msgid "Calibration of extrusion" -#~ msgstr "Калибровка экструзии" +msgid "Show splash screen" +msgstr "Показывать окно приветствия" -#~ msgid "Push new filament into the extruder" -#~ msgstr "Вставка нового прутка в экструдер" - -#~ msgid "Confirm whether the filament has been extruded" -#~ msgstr "Подтвердите, что пластиковая нить была выдавлена" - -#~ msgid "The region parameter is incorrrect" -#~ msgstr "Неправильная региональная настройка" - -#~ msgid "Failure of printer login" -#~ msgstr "Не удалось подключиться к принтеру." - -#~ msgid "Failed to get ticket" -#~ msgstr "Не удалось получить заявку" - -#~ msgid "User authorization timeout" -#~ msgstr "Таймаут авторизации пользователя" - -#~ msgid "Failure of bind" -#~ msgstr "Ошибка привязки" - -#~ msgid "Print file not found, please slice again" -#~ msgstr "Файл для печати не найден, нарежьте ещё раз." - -#~ msgid "Failed uploading print file" -#~ msgstr "Не удалось передать файл на печать." - -#~ msgid "Wrong Access code" -#~ msgstr "Неправильный код доступа" - -#~ msgid "Send to Printer failed. Please try again." -#~ msgstr "Ошибка отправки на принтер. Пожалуйста, попробуйте ещё раз." - -#~ msgid "No space left on Printer SD card" -#~ msgstr "На SD-карте принтера недостаточно места" - -#~ msgid "Sending gcode file through cloud service" -#~ msgstr "Отправка файла G-кода через облачный сервис" - -#~ msgid "Please log out and login to the printer again." -#~ msgstr "Пожалуйста, выйдите и снова войдите в принтер." - -#~ msgid "Factors of dynamic flow cali" -#~ msgstr "Коэф. калиб. динам. потока" - -#~ msgid "" -#~ "There are currently no identical spare consumables available, and " -#~ "automatic replenishment is currently not possible. \n" -#~ "(Currently supporting automatic supply of consumables with the same " -#~ "brand, material type, and color)" -#~ msgstr "" -#~ "В настоящее время идентичные резервные материалы отсутствуют, и " -#~ "автоматическое пополнение их в настоящее время невозможно. \n" -#~ "(В настоящее время поддерживается автоматическая поставка расходных " -#~ "материалов того же производителя, типа материала и цвета)." - -#, c-format, boost-format -#~ msgid "" -#~ "Bed temperature of other layer is lower than bed temperature of initial " -#~ "layer for more than %d degree centigrade.\n" -#~ "This may cause model broken free from build plate during printing" -#~ msgstr "" -#~ "Температура стола для последующих слоёв слишком низкая по сравнению с " -#~ "температурой первого слоя, более чем на %d градусов Цельсия.\n" -#~ "Это может привести к отрыву модели от стола во время печати." - -#~ msgid "" -#~ "Bed temperature is higher than vitrification temperature of this " -#~ "filament.\n" -#~ "This may cause nozzle blocked and printing failure\n" -#~ "Please keep the printer open during the printing process to ensure air " -#~ "circulation or reduce the temperature of the hot bed" -#~ msgstr "" -#~ "Температура стола выше температуры стеклования этой пластиковой нити.\n" -#~ "Это может привести к засорению сопла и сбою печати.\n" -#~ "Пожалуйста, держите принтер открытым во время печати, чтобы обеспечить " -#~ "циркуляцию воздуха или снизить температуру стола." - -#~ msgid "Invalid nozzle diameter" -#~ msgstr "Недопустимый диаметр сопла" - -#~ msgid "Fan Speed: " -#~ msgstr "Скорость вентилятора: " - -#~ msgid "Total Time Estimation" -#~ msgstr "Оценка общего времени" - -#~ msgid "Resonance frequency identification" -#~ msgstr "Идентификация резонансной частоты" - -#~ msgid "Initialize failed (Not supported with LAN-only mode)!" -#~ msgstr "Ошибка инициализации (не поддерживается в режиме «Только LAN»)!" - -#~ msgid "Initialize failed (Not supported by printer)!" -#~ msgstr "Ошибка инициализации (не поддерживается принтером)!" - -#~ msgid "" -#~ "Another virtual camera is running.\n" -#~ "Bambu Studio supports only a single virtual camera.\n" -#~ "Do you want to stop this virtual camera?" -#~ msgstr "" -#~ "Уже работает одна виртуальная камера.\n" -#~ "Bambu Studio поддерживает только одну виртуальную камеру.\n" -#~ "Хотите остановить эту виртуальную камеру?" - -#~ msgid "Not supported by this model of printer!" -#~ msgstr "Не поддерживается этой моделью принтера!" - -#~ msgid "No files" -#~ msgstr "Файлы отсутствуют" - -#~ msgid "Not accessible in LAN-only mode!" -#~ msgstr "Недоступно в режиме «Только LAN»!" - -#~ msgid "Missing LAN ip of printer!" -#~ msgstr "Отсутствует сетевой адрес принтера!" - -#, c-format, boost-format -#~ msgid "You are going to delete %u files. Are you sure to continue?" -#~ msgstr "" -#~ "Вы собираетесь удалить файлы: %u шт. Вы уверены, что хотите это сделать?" - -#~ msgid "" -#~ "The .gcode.3mf file contains no G-code data.Please slice it whthBambu " -#~ "Studio and export a new .gcode.3mf file." -#~ msgstr "" -#~ "Файл .gcode.3mf не содержит G-кода. Пожалуйста, нарежьте его в программе " -#~ "Bambu Studio и экспортируйте новый файл .gcode.3mf." - -#~ msgid "Immediately score" -#~ msgstr "Оценить сейчас" - -#, c-format, boost-format -#~ msgid "" -#~ "Disconnected from printer [%s] due to LAN mode disabled.Please reconnect " -#~ "the printer by logging in with your user account." -#~ msgstr "" -#~ "Соединение с принтером [%s] разорвано из-за отключения режима «Только " -#~ "LAN». Повторно подключитесь к принтеру, войдя в свою учётную запись." - -#, c-format, boost-format -#~ msgid "" -#~ "Disconnected from printer [%s] due to LAN mode enabled.Please reconnect " -#~ "the printer by inputting Access Code which can be gotten from printer " -#~ "screen." -#~ msgstr "" -#~ "Соединение с принтером [%s] разорвано из-за включения режима «Только " -#~ "LAN». Повторно подключитесь к принтеру, введя код доступа, который можно " -#~ "получить на экране принтера." - -#~ msgid "Please give a score for your favorite Bambu Market model." -#~ msgstr "" -#~ "Пожалуйста, поставьте оценку вашей любимой модели в магазине Bambu Market." - -#~ msgid "Score" -#~ msgstr "Рейтинг" - -#~ msgid "" -#~ "The bed temperature exceeds filament's vitrification temperature. Please " -#~ "open the front door of printer before printing to avoid nozzle clog." -#~ msgstr "" -#~ "Температура стола превышает температуру стеклования пластиковой нити. " -#~ "Пожалуйста, откройте переднюю дверцу принтера перед печатью, чтобы " -#~ "избежать засорения сопла." - -#~ msgid "The 3mf is not from Bambu Lab, load geometry data only." -#~ msgstr "" -#~ "Этот 3mf создан не в Bambu Lab, поэтому загрузятся только данные " -#~ "геометрии." - -#~ msgid "Online Models" -#~ msgstr "Онлайн-модели" - -#~ msgid "Show online staff-picked models on the home page" -#~ msgstr "Показывать отобранные сотрудниками модели на главной странице" - -#~ msgid "" -#~ "Upload task timed out. Please check the network problem and try again" -#~ msgstr "" -#~ "Истекло время ожидания отправки задания. Проверьте сетевое подключение и " -#~ "повторите попытку." - -#~ msgid "Bamabu Engineering Plate" -#~ msgstr "Инженерная пластина Bamabu" - -#~ msgid "Bamabu High Temperature Plate" -#~ msgstr "Высокотемпературная пластина Bamabu" - -#~ msgid "Can't connect to the printer" -#~ msgstr "Не удаётся подключиться к принтеру" - -#~ msgid "The printer is required to be in the same LAN as Bambu Studio." -#~ msgstr "Принтер должен находиться в одной локальной сети с Bambu Studio." - -#~ msgid "Recommended temperature range" -#~ msgstr "Рекомендуемый диапазон температур" - -#~ msgid "High Temp Plate" -#~ msgstr "Высокотемпературная пластина" - -#~ msgid "" -#~ "Bed temperature when high temperature plate is installed. Value 0 means " -#~ "the filament does not support to print on the High Temp Plate" -#~ msgstr "" -#~ "Температура стола при установленной высокотемпературной печатной " -#~ "пластине. 0 означает, что пластиковая нить не поддерживает печать на этой " -#~ "печатной пластине." - -#~ msgid "Switch between Prepare/Prewview" -#~ msgstr "Переключение между окном подготовки и окном предпросмотра нарезки" - -#~ msgid "" -#~ "Click OK to update the Network plug-in when Bambu Studio launches next " -#~ "time." -#~ msgstr "" -#~ "Нажмите OK, чтобы обновить сетевой плагин при следующем запуске Bambu " -#~ "Studio." - -#~ msgid "New version of Bambu Studio" -#~ msgstr "Доступна новая версия Bambu Studio" - -#~ msgid "" -#~ "Step 1, please confirm Bambu Studio and your printer are in the same LAN." -#~ msgstr "" -#~ "Шаг 1. Пожалуйста, убедитесь, что Bambu Studio и ваш принтер находятся в " -#~ "одной локальной сети." - -#~ msgid "Internal bridge support thickness" -#~ msgstr "Толщина поддержки внутреннего моста" - -#~ msgid "" -#~ "If enabled, support loops will be generated under the contours of " -#~ "internal bridges.These support loops could prevent internal bridges from " -#~ "extruding over the air and improve the top surface quality, especially " -#~ "when the sparse infill density is low.This value determines the thickness " -#~ "of the support loops. 0 means disable this feature" -#~ msgstr "" -#~ "Если включено, под контурами внутренних мостов будут создаваться петли " -#~ "поддержки. Эти петли поддержки могут препятствовать выдавливанию " -#~ "внутренних мостов в воздух и улучшить качество поверхности сверху, " -#~ "особенно при низкой плотности заполнения. Это значение определяет толщину " -#~ "петель поддержки. Установите 0 для отключения." - -#~ msgid "Temperature of vitrificaiton" -#~ msgstr "Температура стеклования" - -#~ msgid "" -#~ "Material becomes soft at this temperature. Thus the heatbed cannot be " -#~ "hotter than this tempature" -#~ msgstr "" -#~ "При этой температуре материал становится мягким. Таким образом, " -#~ "подогреваемый стол не может быть горячее этой температуры." - -#~ msgid "" -#~ "Acceleration of initial layer. Using a lower value can improve build " -#~ "plate adhensive" -#~ msgstr "" -#~ "Ускорение на первом слое. Использование более низкого значения может " -#~ "улучшить адгезию к столу." - -#~ msgid "" -#~ "Klipper's max_accel_to_decel will be adjusted to this % of acceleration" -#~ msgstr "" -#~ "Значение Klipper-а max_accel_to_decel (ограничение ускорения зигзагов) " -#~ "будет скорректировано на данное ускорение: %" - -#~ msgid "" -#~ "Filter out gaps smaller than the threshold specified. This setting won't " -#~ "affect top/bottom layers" -#~ msgstr "" -#~ "Небольшие промежутки меньше указанного порога не будут заполняться. Этот " -#~ "параметр не влияет на верхнюю/нижнюю поверхность." - -#~ msgid "Enable this option if machine has auxiliary part cooling fan" -#~ msgstr "" -#~ "Включите, если в принтере имеет вспомогательный вентилятор для охлаждения " -#~ "моделей." - -#~ msgid "Maximum acceleration for travel (M204 T)" -#~ msgstr "Максимальное ускорение при перемещении (M204 T)" - -#~ msgid "" -#~ "Speed of auxiliary part cooling fan. Auxiliary fan will run at this speed " -#~ "during printing except the first several layers which is defined by no " -#~ "cooling layers" -#~ msgstr "" -#~ "Скорость вращения вспомогательного вентилятора для охлаждения моделей. Он " -#~ "всегда будет работать с этой скоростью, за исключением первых нескольких " -#~ "слоёв, которые обычно настроены на работу без охлаждения." - -#~ msgid "The minimum printing speed when slow down for cooling" -#~ msgstr "" -#~ "Минимальная скорость печати при которой происходит сброс скорости для " -#~ "лучшего охлаждения." - -#~ msgid "" -#~ "Style and shape of the support. For normal support, projecting the " -#~ "supports into a regular grid will create more stable supports (default), " -#~ "while snug support towers will save material and reduce object scarring.\n" -#~ "For tree support, slim style will merge branches more aggressively and " -#~ "save a lot of material (default), while hybrid style will create similar " -#~ "structure to normal support under large flat overhangs." -#~ msgstr "" -#~ "Стиль и форма создаваемой поддержки.\n" -#~ "\n" -#~ "Стиль «Сетка» создаёт более устойчивые опоры. Стиль «Аккуратный» экономит " -#~ "материал и уменьшает образование дефектов на моделях.\n" -#~ "\n" -#~ "Для древовидной поддержки, при стройном стиле происходит более " -#~ "агрессивное объединение ветвей и экономия материала (по умолчанию). В то " -#~ "время как гибридный стиль создаёт структуру, схожую с обычную поддержкой " -#~ "при больших плоских нависаниях." - -#~ msgid "Target chamber temperature" -#~ msgstr "Температура, которую необходимо поддерживать внутри принтера." - -#~ msgid "Bed temperature difference" -#~ msgstr "Разница температур подогреваемого стола" - -#~ msgid "" -#~ "Do not recommend bed temperature of other layer to be lower than initial " -#~ "layer for more than this threshold. Too low bed temperature of other " -#~ "layer may cause the model broken free from build plate" -#~ msgstr "" -#~ "Не рекомендуется, чтобы температура последующих слоёв была ниже " -#~ "температуры первого слоя, более чем на это пороговое значение. Слишком " -#~ "низкая температура последующих слоёв может привести к отрыву модели от " -#~ "стола." - -#~ msgid "Orient the model" -#~ msgstr "Ориентация модели" - -#~ msgid "Empty layers around bottom are replaced by nearest normal layers." -#~ msgstr "" -#~ "Пустые слои обнаруженные на дне модели были заменены ближайшими " -#~ "нормальными слоями." - -#~ msgid "The model has too many empty layers." -#~ msgstr "Модель имеет слишком много пустых слоев." - -#~ msgid "Send to print" -#~ msgstr "Отправить на печать" - -#~ msgid "Error Message" -#~ msgstr "Сообщение об ошибке" - -#~ msgid "Error uploading to print host:" -#~ msgstr "Ошибка при отправке на хост печати:" - -#~ msgid "" -#~ "Please input valid values:\n" -#~ "start > 0 step >= 0\n" -#~ "end > start + step)" -#~ msgstr "" -#~ "Введите допустимые значения:\n" -#~ "start > 0 step >= 0\n" -#~ "end > start + step)" - -#~ msgid "" -#~ "Please input valid values:\n" -#~ "start > 10 step >= 0\n" -#~ "end > start + step)" -#~ msgstr "" -#~ "Введите допустимые значения:\n" -#~ "start > 10 step >= 0\n" -#~ "end > start + step)" - -#~ msgid "Left Preset Value" -#~ msgstr "Значение в левом профиле" - -#~ msgid "Right Preset Value" -#~ msgstr "Значение в правом профиле" - -#~ msgid "Undef category" -#~ msgstr "Неопределённая категория" - -#~ msgid "Undef group" -#~ msgstr "Неопределённая группа" - -#~ msgid "Retraction Length (Toolchange)" -#~ msgstr "Длина отката (при смене инструмента)" - -#~ msgid "Flow Dynamic" -#~ msgstr "Динамика потока" - -#~ msgid "A fatal error occurred: %1%" -#~ msgstr "Произошла фатальная ошибка: %1%" - -#~ msgid "Record" -#~ msgstr "Запись" - -#~ msgid "Report issue" -#~ msgstr "Сообщить о проблеме" - -#~ msgid "Connection to OctoPrint/Klipper works correctly." -#~ msgstr "Соединение с OctoPrint/Klipper успешно установлено." - -#~ msgid "Could not connect to OctoPrint/Klipper" -#~ msgstr "Не удаётся подключиться к OctoPrint/Klipper" - -#~ msgid "Connection to FlashAir works correctly." -#~ msgstr "Соединение с FlashAir успешно установлено." - -#~ msgid "Could not connect to FlashAir" -#~ msgstr "Не удаётся подключиться к FlashAir" - -#~ msgid "Connection to Duet works correctly." -#~ msgstr "Соединение с Duet успешно установлено." - -#~ msgid "Could not connect to Duet" -#~ msgstr "Не удалось подключиться к Duet" - -#~ msgid "Connection to AstroBox works correctly." -#~ msgstr "Соединение с AstroBox успешно установлено." - -#~ msgid "Could not connect to AstroBox" -#~ msgstr "Не удалось подключиться к AstroBox" - -#~ msgid "Connection to Repetier works correctly." -#~ msgstr "Подключение к Repetier успешно установлено." - -#~ msgid "Could not connect to Repetier" -#~ msgstr "Не удалось подключиться к Repetier" - -#~ msgid "Connection to MKS works correctly." -#~ msgstr "Подключение к MKS успешно установлено." - -#~ msgid "Could not connect to MKS" -#~ msgstr "Не удалось подключиться к MKS" - -#~ msgid "Connection to PrusaLink works correctly." -#~ msgstr "Подключение к PrusaLink установлено." - -#~ msgid "Could not connect to PrusaLink" -#~ msgstr "Не удалось подключиться к PrusaLink" - -#~ msgid "Note: OctoPrint version at least 1.1.0 is required." -#~ msgstr "Примечание: требуется версия OctoPrint не ниже 1.1.0." - -#~ msgid "Connection refused" -#~ msgstr "Соединение запрещено" - -#~ msgid "Failed to connect to %s port %ld: %s" -#~ msgstr "Не удалось подключиться к порту %s %ld: %s" - -#~ msgid "Couldn't connect to server" -#~ msgstr "Не удалось подключиться к серверу" - -#~ msgid "Couldn't resolve host '%s'" -#~ msgstr "Не удаётся определить имя хоста '%s'" - -#~ msgid "Suggestion" -#~ msgstr "Совет" - -#~ msgid "" -#~ "BambuSource has not correctly been registered for media playing! Press " -#~ "Yes to re-register it." -#~ msgstr "" -#~ "Компонент BambuSource неправильно зарегистрирован для воспроизведения " -#~ "мультимедиа! Нажмите «Да», чтобы перерегистрировать его." - -#~ msgid "" -#~ "Missing BambuSource component registered for media playing! Please re-" -#~ "install BambuStutio or seek after-sales help." -#~ msgstr "" -#~ "Отсутствует компонент BambuSource, зарегистрированный для воспроизведения " -#~ "мультимедиа! Пожалуйста, переустановите BambuStutio или обратитесь в " -#~ "поддержку." - -#~ msgid "Test storage" -#~ msgstr "Тест накопителя" - -#~ msgid "Test BambuLab" -#~ msgstr "Тест BambuLab" - -#~ msgid "Test BambuLab:" -#~ msgstr "Тест BambuLab:" - -#~ msgid "Test Bing.com" -#~ msgstr "Тест Bing.com" - -#~ msgid "Test bing.com:" -#~ msgstr "Тест bing.com:" - -#~ msgid "Test HTTP" -#~ msgstr "Тест HTTP" - -#~ msgid "Test HTTP Service:" -#~ msgstr "Тест HTTP сервера:" - -#~ msgid "Test storage upgrade" -#~ msgstr "Тест накопителя (обновление)" - -#~ msgid "Test Storage Upgrade:" -#~ msgstr "Тест накопителя (обновление):" - -#~ msgid "Test Storage Upload" -#~ msgstr "Тест накопителя (отправка)" - -#~ msgid "Test Storage Upload:" -#~ msgstr "Тест накопителя (отправка):" - -#~ msgid "Test storage download" -#~ msgstr "Тест накопителя (загрузка)" - -#~ msgid "Test Storage Download:" -#~ msgstr "Тест накопителя (загрузка):" - -#~ msgid "Log Info" -#~ msgstr "Журнал сведений" - -#~ msgid "Studio Version:" -#~ msgstr "Версия программы:" - -#~ msgid "System Version:" -#~ msgstr "Версия ОС:" - -#~ msgid "Start Test Multi-Thread" -#~ msgstr "Запуск многопоточного теста" - -#~ msgid "Start Test Single-Thread" -#~ msgstr "Запуск однопоточного теста" - -#~ msgid "Network Test" -#~ msgstr "Проверка сети" - -#~ msgid "Wipe tower - Purging volume adjustment" -#~ msgstr "Черновая башня - регулировка объёма сброса пластика" - -#~ msgid "" -#~ "Here you can adjust required purging volume (mm³) for any given pair of " -#~ "tools." -#~ msgstr "" -#~ "Здесь вы можете отрегулировать требуемый объём очистки (мм³) для любой " -#~ "пары инструментов." - -#~ msgid "Extruder changed to" -#~ msgstr "Экструдер перешёл на - " - -#~ msgid "Tool #" -#~ msgstr "Инструмент #" - -#~ msgid "" -#~ "Total purging volume is calculated by summing two values below, depending " -#~ "on which tools are loaded/unloaded." -#~ msgstr "" -#~ "Общий объём прочистки вычисляется путём суммирования двух нижеуказанных " -#~ "значений, в зависимости от того, какие инструменты предзагружены/" -#~ "выгружены." - -#~ msgid "Volume to purge (mm³) when the filament is being" -#~ msgstr "Объём прочистки (мм³) при выдавливании прутка" - -#~ msgid "Volumetric speed" -#~ msgstr "Объёмная скорость потока" - -#~ msgid "Branch Diameter" -#~ msgstr "Диаметр ветвей" - -#~ msgid "" -#~ "The diameter of the thinnest branches of organic support. Thicker " -#~ "branches are more sturdy. Branches towards the base will be thicker than " -#~ "this." -#~ msgstr "" -#~ "Диаметр самых тонких ветвей органической поддержки. Чем толще ветви, тем " -#~ "они крепче. Ветви, идущие к основанию, будут утолщаться." - -#~ msgid "Branch Distance" -#~ msgstr "Расстояние между ветками" - -#~ msgid "" -#~ "How far apart the branches need to be when they touch the model. Making " -#~ "this distance small will cause the tree support to touch the model at " -#~ "more points, causing better overhang but making support harder to remove." -#~ msgstr "" -#~ "Указывает, насколько далеко друг от друга должны располагаться ветви при " -#~ "касании модели. Если задать небольшое расстояние, то увеличится " -#~ "количество точек, в которых древовидная поддержка касается модели. Это " -#~ "улучшит печать нависаний, но при этом усложнит удаление поддержки." - -# Нижняя граница сработки??? -#~ msgid "Z hop lower boundary" -#~ msgstr "Приподнимать ось Z только ниже" - -#~ msgid "" -#~ "Z hop will only come into effect when Z is above this value and is below " -#~ "the parameter: \"Z hop upper boundary\"" -#~ msgstr "" -#~ "Если указать положительное значение, ось Z будет подниматься только ниже " -#~ "(до) заданной здесь высоты (высота считается от стола). Таким образом вы " -#~ "можете запретить подъём оси Z выше установленной высоты." - -# Верхняя граница сработки??? -#~ msgid "Z hop upper boundary" -#~ msgstr "Приподнимать ось Z только выше" - -#~ msgid "" -#~ "If this value is positive, Z hop will only come into effect when Z is " -#~ "above the parameter: \"Z hop lower boundary\" and is below this value" -#~ msgstr "" -#~ "Если указать положительное значение, ось Z будет подниматься только выше " -#~ "(после) заданной здесь высоты (высота считается от стола). Таким образом " -#~ "вы можете отключить подъём оси Z при печати на первых слоях (в начале " -#~ "печати)." +msgid "Show the splash screen during startup." +msgstr "Показывать окно приветствия при запуске приложения." diff --git a/localization/i18n/tr/OrcaSlicer_tr.po b/localization/i18n/tr/OrcaSlicer_tr.po index dd13d3169a..042b8ab5d8 100644 --- a/localization/i18n/tr/OrcaSlicer_tr.po +++ b/localization/i18n/tr/OrcaSlicer_tr.po @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: OrcaSlicer\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-11-10 14:54+0800\n" -"PO-Revision-Date: 2023-10-07 12:57+0300\n" -"Last-Translator: Sadri Ercan\n" +"PO-Revision-Date: 2023-11-15 00:35+0300\n" +"Last-Translator: Olcay ÖREN\n" "Language-Team: Türkçe\n" "Language: tr_TR\n" "MIME-Version: 1.0\n" @@ -170,10 +170,10 @@ msgid "Height Range" msgstr "Yükseklik Aralığı" msgid "Vertical" -msgstr "" +msgstr "Dikey" msgid "Horizontal" -msgstr "" +msgstr "Yatay" msgid "Remove painted color" msgstr "Boyalı rengi kaldır" @@ -656,6 +656,10 @@ msgid "" "Please note, application settings will be lost, but printer profiles will " "not be affected." msgstr "" +"OrcaSlicer konfigürasyon dosyası bozulmuş olabilir ve ayrıştırılamayabilir.\n" +"OrcaSlicer, konfigürasyon dosyasını yeniden oluşturmayı denedi.\n" +"Lütfen dikkat edin, uygulama ayarları kaybolacak, ancak yazıcı profilleri " +"etkilenmeyecek." msgid "Rebuild" msgstr "Yeniden yükleniyor" @@ -804,7 +808,7 @@ msgid "Fuzzy Skin" msgstr "Bulanık Kaplama" msgid "Extruders" -msgstr "Ekstrüderler" +msgstr "Ekstruderler" msgid "Extrusion Width" msgstr "Ekstrüzyon Genişliği" @@ -1093,7 +1097,7 @@ msgid "Change Filament" msgstr "Filamenti Değiştir" msgid "Set Filament for selected items" -msgstr "Seçilen öğeler için Filamenti Ayarla" +msgstr "Seçilen öğeler için Filamenti ayarla" msgid "current" msgstr "geçerli" @@ -1575,7 +1579,7 @@ msgid "Pull back current filament" msgstr "Mevcut filamenti geri çekin" msgid "Push new filament into extruder" -msgstr "Yeni filamanı ekstrüdere itin" +msgstr "Yeni filamenti ekstrudere itin" msgid "Purge old filament" msgstr "Eski filamenti temizleyin" @@ -2090,7 +2094,7 @@ msgid "Disable AMS" msgstr "AMS'yi devre dışı bırak" msgid "Print with the filament mounted on the back of chassis" -msgstr "Şasinin arkasına monte edilmiş filamanla yazdırma" +msgstr "Şasinin arkasına monte edilmiş filamentle yazdırma" msgid "Cabin humidity" msgstr "Kabin nemi" @@ -2162,11 +2166,11 @@ msgid "Group" msgstr "Grup" msgid "The printer does not currently support auto refill." -msgstr "" +msgstr "Yazıcı şu anda otomatik yeniden doldurmayı desteklemiyor." msgid "" "AMS filament backup is not enabled, please enable it in the AMS settings." -msgstr "" +msgstr "AMS filament yedekleme özelliği etkin değil, lütfen AMS ayarlarından etkinleştirin." msgid "" "If there are two identical filaments in AMS, AMS filament backup will be " @@ -2174,6 +2178,10 @@ msgid "" "(Currently supporting automatic supply of consumables with the same brand, " "material type, and color)" msgstr "" +"Eğer AMS'de iki aynı filament bulunursa, AMS filament yedekleme özelliği " +"etkinleştirilecektir. \n" +"(Şu anda aynı markaya ait sarf malzemelerinin otomatik temini desteklenmektedir, " +"malzeme türü ve rengi)" msgid "AMS Settings" msgstr "AMS Ayarları" @@ -2185,7 +2193,7 @@ msgid "" "The AMS will automatically read the filament information when inserting a " "new Bambu Lab filament. This takes about 20 seconds." msgstr "" -"AMS, yeni bir Bambu Lab filamanı takıldığında filament bilgilerini otomatik " +"AMS, yeni bir Bambu Lab filamenti takıldığında filament bilgilerini otomatik " "olarak okuyacaktır. Bu yaklaşık 20 saniye sürer." msgid "" @@ -2210,7 +2218,7 @@ msgid "" "start-up. It will take about 1 minute.The reading process will roll filament " "spools." msgstr "" -"AMS, başlangıçta takılan filamanın bilgilerini otomatik olarak okuyacaktır. " +"AMS, başlangıçta takılan filamentin bilgilerini otomatik olarak okuyacaktır. " "Yaklaşık 1 dakika sürecektir. Okuma işlemi filament makaralarını saracaktır." msgid "" @@ -2218,7 +2226,7 @@ msgid "" "during startup and will continue to use the information recorded before the " "last shutdown." msgstr "" -"AMS, başlatma sırasında takılan filamandan bilgileri otomatik olarak okumaz " +"AMS, başlatma sırasında takılan filamentden bilgileri otomatik olarak okumaz " "ve son kapatmadan önce kaydedilen bilgileri kullanmaya devam eder." msgid "Update remaining capacity" @@ -2229,7 +2237,7 @@ msgid "" "info is updated. During printing, remaining capacity will be updated " "automatically." msgstr "" -"AMS, filament bilgisi güncellendikten sonra Bambu filamanının kalan " +"AMS, filament bilgisi güncellendikten sonra Bambu filamentin kalan " "kapasitesini tahmin edecek. Yazdırma sırasında kalan kapasite otomatik " "olarak güncellenecektir." @@ -2713,7 +2721,7 @@ msgid "" "45℃.In order to avoid extruder clogging,low temperature filament(PLA/PETG/" "TPU) is not allowed to be loaded." msgstr "" -"Mevcut hazne sıcaklığı veya hedef hazne sıcaklığı 45 ° C'yi aşıyor Ekstrüder " +"Mevcut hazne sıcaklığı veya hedef hazne sıcaklığı 45 ° C'yi aşıyor Ekstruder " "tıkanmasını önlemek için düşük sıcaklıkta filament (PLA / PETG / TPU) " "yüklenmesine izin verilmez." @@ -2722,7 +2730,7 @@ msgid "" "avoid extruder clogging,it is not allowed to set the chamber temperature " "above 45℃." msgstr "" -"Ekstrüdere düşük sıcaklıkta filament (PLA / PETG / TPU) yüklendi. Ekstrüder " +"Ekstrudere düşük sıcaklıkta filament (PLA / PETG / TPU) yüklendi. Ekstruder " "tıkanmasını önlemek için hazne sıcaklığının 45 ° C'nin üzerine ayarlanmasına " "izin verilmez." @@ -2741,15 +2749,16 @@ msgstr "Yazdırma işi başlatılamadı" msgid "" "This calibration does not support the currently selected nozzle diameter" msgstr "" +"Bu kalibrasyon, şu anda seçilen nozzle çapını desteklememektedir" msgid "Current flowrate cali param is invalid" -msgstr "" +msgstr "Geçerli akış hızı cali parametresi geçersiz" msgid "Selected diameter and machine diameter do not match" -msgstr "" +msgstr "Seçilen çap ile makine çapı eşleşmiyor" msgid "Failed to generate cali gcode" -msgstr "" +msgstr "Cali G-code oluşturma başarısız oldu" msgid "Calibration error" msgstr "Kalibrasyon hatası" @@ -2878,7 +2887,7 @@ msgid "Total" msgstr "Toplam" msgid "Total Estimation" -msgstr "" +msgstr "Toplam Tahmini" msgid "Total time" msgstr "Toplam süre" @@ -3962,6 +3971,8 @@ msgid "" "You have completed printing the mall model, \n" "but the synchronization of rating information has failed." msgstr "" +"Mall modelinin baskısını tamamladınız, \n" +"ancak puanlama bilgilerinin senkronizasyonu başarısız oldu." msgid "How do you like this printing file?" msgstr "Bu baskı dosyasını nasıl buldunuz?" @@ -4059,7 +4070,7 @@ msgid "" "unload the filament and try again." msgstr "" "Filament bilgisi okunamıyor: Filament alet kafasına yüklenmiştir, lütfen " -"filamanı boşaltın ve tekrar deneyin." +"filamenti boşaltın ve tekrar deneyin." msgid "This only takes effect during printing" msgstr "Bu yalnızca yazdırma sırasında etkili olur" @@ -4426,22 +4437,22 @@ msgid "Printer settings" msgstr "Yazıcı ayarları" msgid "Remove current plate (if not last one)" -msgstr "" +msgstr "Mevcut tablayı kaldırın (eğer sonuncusu değilse)" msgid "Auto orient objects on current plate" -msgstr "" +msgstr "Mevcut tablada nesneleri otomatik olarak oryante et" msgid "Arrange objects on current plate" -msgstr "" +msgstr "Mevcut tablada nesneleri düzenle" msgid "Unlock current plate" -msgstr "" +msgstr "Mevcut tablanın kilidini aç" msgid "Lock current plate" -msgstr "" +msgstr "Mevcut tablayı kilitle" msgid "Customize current plate" -msgstr "" +msgstr "Mevcut tablayı özelleştir" msgid "Untitled" msgstr "İsimsiz" @@ -4566,6 +4577,9 @@ msgid "" "clogged when printing this filament in a closed enclosure. Please open the " "front door and/or remove the upper glass." msgstr "" +"Mevcut sıcak yatak sıcaklığı oldukça yüksek. Bu filamenti " +"kapalı bir muhafaza içinde bastırırken nozzle tıkanabilir. " +"Lütfen ön kapağı açın ve/veya üst camı çıkarın." msgid "" "The nozzle hardness required by the filament is higher than the default " @@ -4573,7 +4587,7 @@ msgid "" "filament, otherwise, the nozzle will be attrited or damaged." msgstr "" "Filamentin gerektirdiği nozul sertliği, yazıcının varsayılan nozul " -"sertliğinden daha yüksektir. Lütfen sertleşmiş nozülü veya filamanı " +"sertliğinden daha yüksektir. Lütfen sertleşmiş nozülü veya filamenti " "değiştirin, aksi takdirde nozül aşınır veya hasar görür." msgid "" @@ -4588,7 +4602,7 @@ msgid "Loading file: %s" msgstr "Dosya yükleniyor: %s" msgid "The 3mf is not supported by OrcaSlicer, load geometry data only." -msgstr "" +msgstr "OrcaSlicer, 3mf formatını desteklememektedir. Sadece geometri verilerini yükle." msgid "Load 3mf" msgstr "3mf yükle" @@ -4698,13 +4712,13 @@ msgid "Export STL file:" msgstr "STL dosyasını dışa aktar:" msgid "Export AMF file:" -msgstr "" +msgstr "AMF dosyasını dışa aktar:" msgid "Save file as:" msgstr "Farklı kaydet:" msgid "Export OBJ file:" -msgstr "" +msgstr "OBJ dosyasını dışa aktar:" msgid "Delete object which is a part of cut object" msgstr "Kesilen nesnenin bir parçası olan nesneyi silin" @@ -4725,10 +4739,10 @@ msgid "Another export job is running." msgstr "Başka bir ihracat işi yürütülüyor." msgid "Replace from:" -msgstr "" +msgstr "Değiştirilecek olan:" msgid "Unable to replace with more than one volume" -msgstr "" +msgstr "Birden fazla hacimle değiştirme yapılamıyor." msgid "Error during replace" msgstr "Değiştirme sırasında hata" @@ -4743,19 +4757,19 @@ msgid "Please select a file" msgstr "Dosya seçin" msgid "Do you want to replace it" -msgstr "" +msgstr "Değiştirmek istiyor musun" msgid "Message" -msgstr "" +msgstr "Mesah" msgid "Reload from:" -msgstr "" +msgstr "Yeniden yükle:" msgid "Unable to reload:" -msgstr "" +msgstr "Yeniden yüklenemiyor:" msgid "Error during reload" -msgstr "" +msgstr "Yeniden yükleme sırasında hata oluştu" msgid "Slicing" msgstr "Dilimleniyor" @@ -4888,13 +4902,13 @@ msgid "Save G-code file as:" msgstr "G-kod dosyasını şu şekilde kaydedin:" msgid "Save SLA file as:" -msgstr "" +msgstr "SLA dosyasını farklı bir isimle kaydet:" msgid "The provided file name is not valid." -msgstr "" +msgstr "Dosya adı geçerli değil." msgid "The following characters are not allowed by a FAT file system:" -msgstr "" +msgstr "FAT dosya sistemi tarafından izin verilmeyen karakterler:" msgid "Save Sliced file as:" msgstr "Dilimlenmiş dosyayı şu şekilde kaydedin:" @@ -4995,8 +5009,8 @@ msgid "" "still want to do this printing, please set this filament's bed temperature " "to non zero." msgstr "" -"Plaka% d: %s'nin %s(%s) filamanını yazdırmak için kullanılması önerilmez. " -"Eğer yine de bu baskıyı yapmak istiyorsanız, lütfen bu filamanın yatak " +"Plaka% d: %s'nin %s(%s) filamentinı yazdırmak için kullanılması önerilmez. " +"Eğer yine de bu baskıyı yapmak istiyorsanız, lütfen bu filamentin yatak " "sıcaklığını sıfır olmayan bir değere ayarlayın." msgid "Switching the language requires application restart.\n" @@ -5063,13 +5077,13 @@ msgid "Units" msgstr "Birimler" msgid "Home" -msgstr "" +msgstr "Ana Sayfa" msgid "Default Page" -msgstr "" +msgstr "Varsayılan Sayfa" msgid "Set the page opened on startup." -msgstr "" +msgstr "Açılışta açılacak sayfayı ayarlayın." msgid "Zoom to mouse position" msgstr "Fare konumuna yakınlaştır" @@ -5082,16 +5096,16 @@ msgstr "" "yakınlaştırın." msgid "Use free camera" -msgstr "" +msgstr "Serbest kamera kullan" msgid "If enabled, use free camera. If not enabled, use constrained camera." -msgstr "" +msgstr "Etkinleştirilirse serbest kamerayı kullanın. Etkin değilse kısıtlı kamerayı kullanın." msgid "Show splash screen" -msgstr "" +msgstr "Açılış ekranını göster" msgid "Show the splash screen during startup." -msgstr "" +msgstr "Açılış sırasında açılış ekranını göster." msgid "Show \"Tip of the day\" notification after start" msgstr "Başlangıçtan sonra \"Günün ipucu\" bildirimini göster" @@ -5496,7 +5510,7 @@ msgid "PLA Plate" msgstr "PLA Plaka" msgid "Bambu Engineering Plate" -msgstr "" +msgstr "Bambu Mühendislik Plakası" msgid "Bambu Smooth PEI Plate" msgstr "Bambu Pürüzsüz PEI Plaka" @@ -5564,7 +5578,7 @@ msgid "" "Filament %s exceeds the number of AMS slots. Please update the printer " "firmware to support AMS slot assignment." msgstr "" -"%s filamanı AMS yuvası sayısını aşıyor. AMS yuvası atamasını desteklemek " +"%s filamenti AMS yuvası sayısını aşıyor. AMS yuvası atamasını desteklemek " "için lütfen yazıcının ürün yazılımını güncelleyin." msgid "" @@ -5578,7 +5592,7 @@ msgid "" "Filaments to AMS slots mappings have been established. You can click a " "filament above to change its mapping AMS slot" msgstr "" -"AMS slot eşlemelerine yönelik filamanlar oluşturulmuştur. Eşleme AMS " +"AMS slot eşlemelerine yönelik filament oluşturulmuştur. Eşleme AMS " "yuvasını değiştirmek için yukarıdaki filamentlerden birine tıklayabilirsiniz" msgid "" @@ -5593,14 +5607,14 @@ msgid "" "Filament %s does not match the filament in AMS slot %s. Please update the " "printer firmware to support AMS slot assignment." msgstr "" -"%s filamanı, %s AMS yuvasındaki filamanla eşleşmiyor. AMS yuvası atamasını " +"%s filamenti, %s AMS yuvasındaki filamentle eşleşmiyor. AMS yuvası atamasını " "desteklemek için lütfen yazıcının ürün yazılımını güncelleyin." msgid "" "Filament does not match the filament in AMS slot. Please update the printer " "firmware to support AMS slot assignment." msgstr "" -"Filament, AMS yuvasındaki filamanla eşleşmiyor. AMS yuvası atamasını " +"Filament, AMS yuvasındaki filamentle eşleşmiyor. AMS yuvası atamasını " "desteklemek için lütfen yazıcının ürün yazılımını güncelleyin." msgid "" @@ -6032,7 +6046,7 @@ msgstr "" "Bu filamentin önerilen Nozul sıcaklığı aralığı. 0 ayar yok anlamına gelir" msgid "Print chamber temperature" -msgstr "" +msgstr "Baskı odası sıcaklığı" msgid "Print temperature" msgstr "Yazdırma sıcaklığı" @@ -6064,13 +6078,16 @@ msgstr "" "Mühendislik Plakasına yazdırmayı desteklemediği anlamına gelir" msgid "Smooth PEI Plate / High Temp Plate" -msgstr "" +msgstr "Düz PEI Plakası / Yüksek Sıcaklık Plakası" msgid "" "Bed temperature when Smooth PEI Plate/High temperature plate is installed. " "Value 0 means the filament does not support to print on the Smooth PEI Plate/" "High Temp Plate" msgstr "" +"Düz PEI Plakası/Yüksek Sıcaklık Plakası takılığın da yatak sıcaklığı. " +"0 Değeri, filamentin Düz PEI Plakası/Yüksek Sıcaklık Plakası üzerin de baskı yapmayı " +"desteklemediği anlamına gelir." msgid "Textured PEI Plate" msgstr "Dokulu PEI Plaka" @@ -6079,7 +6096,7 @@ msgid "" "Bed temperature when Textured PEI Plate is installed. Value 0 means the " "filament does not support to print on the Textured PEI Plate" msgstr "" -"Dokulu PEI Plaka takıldığında yatak sıcaklığı. 0 Değeri, filamentin Dokulu " +"Dokulu PEI Plaka takıldığın da yatak sıcaklığı. 0 Değeri, filamentin Dokulu " "PEI Plaka üzerine yazdırmayı desteklemediği anlamına gelir" msgid "Volumetric speed limitation" @@ -6143,13 +6160,13 @@ msgid "Wipe tower parameters" msgstr "Silme kulesi parametreleri" msgid "Toolchange parameters with single extruder MM printers" -msgstr "Tek ekstrüderli MM yazıcılarda araç değiştirme parametreleri" +msgstr "Tek ekstruderli MM yazıcılarda araç değiştirme parametreleri" msgid "Ramming settings" msgstr "Sıkıştırma ayarları" msgid "Toolchange parameters with multi extruder MM printers" -msgstr "Çoklu ekstrüder MM yazıcılarda araç değiştirme parametreleri" +msgstr "Çoklu ekstruder MM yazıcılarda araç değiştirme parametreleri" msgid "Printable space" msgstr "Tabla Ayarı" @@ -6161,7 +6178,7 @@ msgid "Fan speed-up time" msgstr "Fan hızlanma süresi" msgid "Extruder Clearance" -msgstr "Ekstrüder Boşluğu" +msgstr "Ekstruder Boşluğu" msgid "Accessory" msgstr "Aksesuar" @@ -6188,7 +6205,7 @@ msgid "Change filament G-code" msgstr "Filament değişimi G-kod" msgid "Change extrusion role G-code" -msgstr "" +msgstr "Ekstrüzyon rolü G-kodu değiştirme" msgid "Pause G-code" msgstr "Duraklatma G-Kod" @@ -6212,13 +6229,13 @@ msgid "Jerk limitation" msgstr "Jerk sınırlaması" msgid "Single extruder multimaterial setup" -msgstr "Tek ekstrüder çoklu malzeme kurulumu" +msgstr "Tek ekstruder çoklu malzeme kurulumu" msgid "Wipe tower" msgstr "Silme kulesi" msgid "Single extruder multimaterial parameters" -msgstr "Tek ekstrüder çoklu malzeme parametreleri" +msgstr "Tek ekstruder çoklu malzeme parametreleri" msgid "Layer height limits" msgstr "Katman yüksekliği sınırları" @@ -6477,7 +6494,7 @@ msgid "" "This is an expert-level setting, incorrect adjustment will likely lead to " "jams, extruder wheel grinding into filament etc." msgstr "" -"Sıkıştırma, tek ekstrüderli bir MM yazıcıda takım değişiminden hemen önce " +"Sıkıştırma, tek ekstruderli bir MM yazıcıda takım değişiminden hemen önce " "yapılan hızlı ekstrüzyonu ifade eder. Amacı, yeni filamentin " "yerleştirilmesini engellememesi ve daha sonra yeniden yerleştirilebilmesi " "için boşaltılmış filamentin ucunu düzgün bir şekilde şekillendirmektir. Bu " @@ -6486,7 +6503,7 @@ msgstr "" "ekstrüzyon hızları ayarlanabilir.\n" "\n" "Bu uzman düzeyinde bir ayardır, yanlış ayarlama muhtemelen sıkışmalara, " -"ekstrüder tekerleğinin filamente sürtünmesine vb. yol açacaktır." +"ekstruder tekerleğinin filamente sürtünmesine vb. yol açacaktır." msgid "Total ramming time" msgstr "Toplam sıkıştırma süresi" @@ -6677,7 +6694,7 @@ msgid "Esc" msgstr "Esc" msgid "keyboard 1-9: set filament for object/part" -msgstr "klavye 1-9: nesne/parça için filamanı ayarlayın" +msgstr "klavye 1-9: nesne/parça için filamenti ayarlayın" msgid "Camera view - Default" msgstr "Kamera görünümü - Varsayılan" @@ -7064,6 +7081,8 @@ msgid "" "Invalid spacing supplied to Flow::with_spacing(), check your layer height " "and extrusion width" msgstr "" +"Flow::with_spacing()'e sağlanan geçersiz boşluk, kat yüksekliğinizi ve " +"ekstrüzyon genişliğinizi kontrol edin" msgid "undefined error" msgstr "bilinmeyen hata" @@ -7328,8 +7347,8 @@ msgid "" "each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to " "layer_gcode." msgstr "" -"Göreceli ekstrüder adreslemesi, kayan nokta doğruluğunun kaybını önlemek " -"için her katmandaki ekstrüder konumunun sıfırlanmasını gerektirir. " +"Göreceli ekstruder adreslemesi, kayan nokta doğruluğunun kaybını önlemek " +"için her katmandaki ekstruder konumunun sıfırlanmasını gerektirir. " "Layer_gcode'a \"G92 E0\" ekleyin." msgid "" @@ -7374,7 +7393,7 @@ msgid "" "polygon by points in following format: \"XxY, XxY, ...\"" msgstr "" "XY düzleminde yazdırılamayan alan. Örneğin X1 Serisi yazıcılar, filament " -"değişimi sırasında filamanı kesmek için sol ön köşeyi kullanır. Alan şu " +"değişimi sırasında filamenti kesmek için sol ön köşeyi kullanır. Alan şu " "formatta noktalarla çokgen olarak ifade edilir: \"XxY, XxY, ...\"" msgid "Bed custom texture" @@ -7393,7 +7412,7 @@ msgstr "" "Fil ayağı etkisini telafi etmek için baskı plakasındaki ilk katmanı küçültün" msgid "Elephant foot compensation layers" -msgstr "" +msgstr "Fil ayağı etkisini telafi katmanları" msgid "" "The number of layers on which the elephant foot compensation will be active. " @@ -7401,6 +7420,10 @@ msgid "" "the next layers will be linearly shrunk less, up to the layer indicated by " "this value." msgstr "" +"Fil ayağı telafisinin etkin olacağı katman sayısı. " +"İlk katman, fil ayak telafisi değeri tarafından küçültülecek, ardından " +"sonraki katmanlar daha az lineer olarak küçültülecek, bu değer tarafından " +"belirtilen katmana kadar." msgid "layers" msgstr "katmanlar" @@ -7743,7 +7766,6 @@ msgstr "" msgid "One wall threshold" msgstr "Tek duvar eşiği" -#, c-format, boost-format msgid "" "If a top surface has to be printed and it's partially covered by another " "layer, it won't be considered at a top layer where its width is below this " @@ -7754,14 +7776,14 @@ msgid "" "on the next layer, like letters. Set this setting to 0 to remove these " "artifacts." msgstr "" -"If a top surface has to be printed and it's partially covered by another " -"layer, it won't be considered at a top layer where its width is below this " -"value. This can be useful to not let the 'one perimeter on top' trigger on " -"surface that should be covered only by perimeters. This value can be a mm or " -"a % of the perimeter extrusion width.\n" -"Warning: If enabled, artifacts can be created is you have some thin features " -"on the next layer, like letters. Set this setting to 0 to remove these " -"artifacts." +"Eğer bir üst yüzey basılacaksa ve kısmen başka bir katman tarafından kaplıysa " +"layer genişliği bu değerin altında olan bir üst katman olarak " +"değerlendirilmeyecek. Yalnızca çevrelerle kaplanması gereken yüzeyde 'bir çevre üstte' " +"tetiklemesine izin vermemek yararlı olabilir. Bu değer mm veya " +"a % çevre ekstrüzyon genişliğinin bir yüzdesi olabilir.\n" +"Uyarı: Etkinleştirilirse bir sonraki katmanda harfler gibi bazı ince özelliklerin " +"olması durumunda yapay yapılar oluşturulabilir. Bu yapıları kaldırmak için bu ayarı 0 " +"olarak ayarlayın." msgid "Only one wall on first layer" msgstr "İlk katmanda yalnızca bir duvar" @@ -7784,29 +7806,34 @@ msgstr "" "yolları (perimeter) oluşturun. " msgid "Reverse on odd" -msgstr "" +msgstr "Tek sayıyı tersine çevir" msgid "Overhang reversal" -msgstr "" +msgstr "Çıkıntıyı tersine çevir" msgid "" "Extrude perimeters that have a part over an overhang in the reverse " "direction on odd layers. This alternating pattern can drastically improve " "steep overhang." msgstr "" +"Tek katmanlarda ters yönde bir çıkıntının üzerinde bir kısmı bulunan " +"çevreleri ekstrüzyonla çıkarın. Bu değişen desen, dik eğimli çıkıntıları önemli " +"ölçüde iyileştirebilir." msgid "Reverse threshold" -msgstr "" +msgstr "Ters eşik" msgid "Overhang reversal threshold" -msgstr "" +msgstr "Çıkıntıyı tersine çevirme eşiği" -#, c-format, boost-format msgid "" "Number of mm the overhang need to be for the reversal to be considered " "useful. Can be a % of the perimeter width.\n" "Value 0 enables reversal on every odd layers regardless." msgstr "" +"Ters çevirmenin faydalı sayılması için çıkıntının mm sayısı " +"olması gerekir. Çevre genişliğinin %'si olabilir.\n" +"Değer 0 her tek katmanda terslemeyi etkinleştirir." msgid "Classic mode" msgstr "Klasik mod" @@ -7978,7 +8005,7 @@ msgid "Activate air filtration" msgstr "Hava filtrelemesini etkinleştirin" msgid "Activate for better air filtration. G-code command: M106 P3 S(0-255)" -msgstr "" +msgstr "Daha iyi hava filtrasyonu için etkinleştirin. G-kodu komutu: M106 P3 S(0-255)" msgid "Fan speed" msgstr "Fan hızı" @@ -8206,7 +8233,7 @@ msgid "" "has slight overflow or underflow" msgstr "" "Malzeme, erimiş hal ile kristal hal arasında geçiş yaptıktan sonra hacimsel " -"değişime sahip olabilir. Bu ayar, bu filamanın gcode'daki tüm ekstrüzyon " +"değişime sahip olabilir. Bu ayar, bu filamentin gcode'daki tüm ekstrüzyon " "akışını orantılı olarak değiştirir. Önerilen değer aralığı 0,95 ile 1,05 " "arasındadır. Belki hafif taşma veya taşma olduğunda güzel düz bir yüzey elde " "etmek için bu değeri ayarlayabilirsiniz" @@ -8284,7 +8311,7 @@ msgid "" "extruded per second. Printing speed is limited by max volumetric speed, in " "case of too high and unreasonable speed setting. Can't be zero" msgstr "" -"Bu ayar, saniyede ne kadar miktarda filamanın eritilip ekstrüde " +"Bu ayar, saniyede ne kadar miktarda filamentin eritilip ekstrude " "edilebileceğini gösterir. Çok yüksek ve makul olmayan hız ayarı durumunda, " "yazdırma hızı maksimum hacimsel hız ile sınırlanır. Sıfır olamaz" @@ -8304,7 +8331,7 @@ msgstr "Filament boşaltma süresi" msgid "Time to unload old filament when switch filament. For statistics only" msgstr "" -"Filamenti değiştirdiğinizde eski filamanı boşaltma zamanı. Yalnızca " +"Filamenti değiştirdiğinizde eski filamenti boşaltma zamanı. Yalnızca " "istatistikler için" msgid "" @@ -8399,7 +8426,7 @@ msgid "" "object, Slic3r will always prime this amount of material into the wipe tower " "to produce successive infill or sacrificial object extrusions reliably." msgstr "" -"Bir takım değişiminden sonra, yeni yüklenen filamanın nozül içindeki kesin " +"Bir takım değişiminden sonra, yeni yüklenen filamentin nozül içindeki kesin " "konumu bilinmeyebilir ve filament basıncı muhtemelen henüz stabil değildir. " "Yazdırma kafasını bir dolguya veya kurban nesneye boşaltmadan önce Slic3r, " "ardışık dolgu veya kurban nesne ekstrüzyonlarını güvenilir bir şekilde " @@ -8451,9 +8478,9 @@ msgid "" "toolchange. This option is only used when the wipe tower is enabled." msgstr "" "Çok takımlı yazıcı kullanırken sıkıştırma gerçekleştirin (yani Yazıcı " -"Ayarları'ndaki 'Tek Ekstrüder Çoklu Malzeme' işaretli olmadığında). " +"Ayarları'ndaki 'Tek Ekstruder Çoklu Malzeme' işaretli olmadığında). " "İşaretlendiğinde, takım değişiminden hemen önce silme kulesinde az miktarda " -"filament hızla ekstrüde edilir. Bu seçenek yalnızca silme kulesi " +"filament hızla ekstrude edilir. Bu seçenek yalnızca silme kulesi " "etkinleştirildiğinde kullanılır." msgid "Multitool ramming volume" @@ -8499,13 +8526,16 @@ msgstr "" "kullanılır" msgid "Softening temperature" -msgstr "" +msgstr "Yumuşama sıcaklığı" msgid "" "The material softens at this temperature, so when the bed temperature is " "equal to or greater than it, it's highly recommended to open the front door " "and/or remove the upper glass to avoid cloggings." msgstr "" +"Filament bu sıcaklıkta yumuşar, bu nedenle yatak sıcaklığı bununla " +"eşit veya daha yüksekse, tıkanmaları önlemek için ön kapağı açmanız " +"ve/veya üst camı çıkarmanız şiddetle önerilir." msgid "Price" msgstr "Fiyat" @@ -8699,6 +8729,7 @@ msgstr "accel_to_decel" msgid "" "Klipper's max_accel_to_decel will be adjusted to this %% of acceleration" msgstr "" +"Klipper'ın max_accel_to_decel değeri ivmenin bu %%'sine göre ayarlanacak" #, c-format, boost-format msgid "%%" @@ -8845,7 +8876,7 @@ msgid "Layers and Perimeters" msgstr "Katmanlar ve Çevreler" msgid "Filter out gaps smaller than the threshold specified" -msgstr "" +msgstr "Belirtilen eşikten daha küçük boşlukları filtrele" msgid "" "Speed of gap infill. Gap usually has irregular line width and should be " @@ -8945,6 +8976,8 @@ msgid "" "Enable this option if machine has auxiliary part cooling fan. G-code " "command: M106 P2 S(0-255)." msgstr "" +"Makinede yardımcı parça soğutma fanı varsa bu seçeneği etkinleştirin. G-code " +"komut: M106 P2 S(0-255)." msgid "" "Start the fan this number of seconds earlier than its target start time (you " @@ -9004,6 +9037,8 @@ msgid "" "This option is enabled if machine support controlling chamber temperature\n" "G-code command: M141 S(0-255)" msgstr "" +"Bu seçenek, hazne sıcaklığını kontrol eden makine desteği varsa etkinleştirilir\n" +"G-code komut: M141 S(0-255)" msgid "Support air filtration" msgstr "Hava filtrasyonu desteği" @@ -9012,6 +9047,8 @@ msgid "" "Enable this if printer support air filtration\n" "G-code command: M106 P3 S(0-255)" msgstr "" +"Yazıcı hava filtrelemeyi destekliyorsa bunu etkinleştirin\n" +"G-code komut: M106 P3 S(0-255)" msgid "G-code flavor" msgstr "G-Kod Uyumu" @@ -9128,7 +9165,7 @@ msgid "Ironing Pattern" msgstr "Ütüleme Deseni" msgid "The pattern that will be used when ironing" -msgstr "" +msgstr "Ütüleme işlemi sırasında kullanılacak desen" msgid "Ironing flow" msgstr "Ütüleme akışı" @@ -9153,11 +9190,11 @@ msgid "Print speed of ironing lines" msgstr "Ütüleme çizgilerinin baskı hızı" msgid "Ironing angle" -msgstr "" +msgstr "Ütüleme açısı" msgid "" -"The angle ironing is done at. A negative number disables this function and " -"uses the default method." +"Ütüleme işleminin gerçekleştiği açı. Negatif bir sayı, bu işlevi devre dışı bırakır ve " +"varsayılan yöntemi kullanır." msgstr "" msgid "This gcode part is inserted at every layer change after lift z" @@ -9287,7 +9324,7 @@ msgid "Maximum acceleration for travel" msgstr "Seyahat için maksimum hızlanma" msgid "Maximum acceleration for travel (M204 T), it only applies to Marlin 2" -msgstr "" +msgstr "Seyahat için maksimum ivme (M204 T), yalnızca Marlin 2 için geçerlidir" msgid "" "Part cooling fan speed may be increased when auto cooling is enabled. This " @@ -9303,7 +9340,7 @@ msgid "" "The largest printable layer height for extruder. Used tp limits the maximum " "layer hight when enable adaptive layer height" msgstr "" -"Ekstrüder için yazdırılabilir en büyük katman yüksekliği. Uyarlanabilir " +"Ekstruder için yazdırılabilir en büyük katman yüksekliği. Uyarlanabilir " "katman yüksekliği etkinleştirildiğinde maksimum katman yüksekliğini " "sınırlamak için kullanılır" @@ -9343,7 +9380,7 @@ msgstr "" "ekstrüzyona geçtiğinde veya tam tersi olduğunda meydana gelen ani ekstrüzyon " "hızı değişikliklerini düzeltir.\n" "\n" -"Ekstrüde edilen hacimsel akışın mm3/sn cinsinden zaman içinde değişebileceği " +"Ekstrude edilen hacimsel akışın mm3/sn cinsinden zaman içinde değişebileceği " "maksimum oranı tanımlar. Daha yüksek değerler, daha yüksek ekstrüzyon hızı " "değişikliklerine izin verildiği anlamına gelir ve daha hızlı hız geçişlerine " "neden olur.\n" @@ -9359,7 +9396,7 @@ msgstr "" "olmak için yeterli yumuşatmaya izin verir.\n" "\n" "Basınç avansı olmayan daha yavaş yazıcılar için değer çok daha düşük " -"ayarlanmalıdır. Doğrudan tahrikli ekstrüderler için 10-15mm3/s2 ve Bowden " +"ayarlanmalıdır. Doğrudan tahrikli ekstruderler için 10-15mm3/s2 ve Bowden " "tarzı için 5-10mm3/s2 değeri iyi bir başlangıç noktasıdır. \n" "\n" "Bu özellik Prusa slicer'da Basınç Dengeleyici olarak bilinir.\n" @@ -9401,6 +9438,11 @@ msgid "" "Please enable auxiliary_fan in printer settings to use this feature. G-code " "command: M106 P2 S(0-255)" msgstr "" +"Yardımcı parça soğutma fanının hızı. Yardımcı fan, soğutma katmanlarının " +"bulunmadığı ilk birkaç katman dışında, yazdırma sırasında bu hızda " +"çalışacaktır.\n" +"Lütfen bu özelliği kullanmak için yazıcı ayarlarında yardımcı_fan'ı etkinleştirin. G-code " +"komut: M106 P2 S(0-255)" msgid "Min" msgstr "Min" @@ -9421,6 +9463,9 @@ msgid "" "cooling is enabled, when printing overhangs and when feature speeds are not " "specified explicitly." msgstr "" +"Daha iyi katman soğutması için yavaşlama etkinleştirildiğinde, yazdırma " +"çıkıntıları olduğunda ve özellik hızları açıkça belirtilmediğinde " +"filament için minimum yazdırma hızı." msgid "Nozzle diameter" msgstr "Nozul çapı" @@ -9458,7 +9503,7 @@ msgid "Cooling tube position" msgstr "Soğutma borusu konumu" msgid "Distance of the center-point of the cooling tube from the extruder tip." -msgstr "Soğutma borusunun merkez noktasının ekstrüder ucundan uzaklığı." +msgstr "Soğutma borusunun merkez noktasının ekstruder ucundan uzaklığı." msgid "Cooling tube length" msgstr "Soğutma borusu uzunluğu" @@ -9478,7 +9523,7 @@ msgid "" msgstr "" "Hızlı sıkıştırma hızlarına izin vermek ve kötü kesilmiş bir filament " "yüklerken direncin üstesinden gelmek için filament değişim sırası sırasında " -"ekstrüder motor akımını artırmak faydalı olabilir." +"ekstruder motor akımını artırmak faydalı olabilir." msgid "Filament parking position" msgstr "Filament park konumu" @@ -9487,7 +9532,7 @@ msgid "" "Distance of the extruder tip from the position where the filament is parked " "when unloaded. This should match the value in printer firmware." msgstr "" -"Ekstrüder ucunun, boşaltıldığında filamentin park edildiği konumdan " +"Ekstruder ucunun, boşaltıldığında filamentin park edildiği konumdan " "uzaklığı. Bu ayar yazıcı ürün yazılımındaki değerle eşleşmelidir." msgid "Extra loading distance" @@ -9785,7 +9830,7 @@ msgid "" "Speed for reloading filament into extruder. Zero means same speed with " "retraction" msgstr "" -"Filamenti ekstrüdere yeniden yükleme hızı. Sıfır, geri çekilmeyle aynı hız " +"Filamenti ekstrudere yeniden yükleme hızı. Sıfır, geri çekilmeyle aynı hız " "anlamına gelir" msgid "Use firmware retraction" @@ -9864,7 +9909,7 @@ msgid "" "inward movement is executed before the extruder leaves the loop." msgstr "" "Kapalı döngü ekstrüzyonda dikişin görünürlüğünü en aza indirmek için, " -"ekstrüder döngüden ayrılmadan önce içeriye doğru küçük bir hareket " +"ekstruder döngüden ayrılmadan önce içeriye doğru küçük bir hareket " "gerçekleştirilir." msgid "Wipe speed" @@ -9988,7 +10033,7 @@ msgid "Use single nozzle to print multi filament" msgstr "Çoklu filament basmak için tek nozul kullan" msgid "Manual Filament Change" -msgstr "" +msgstr "Manuel Filament Değişimi" msgid "" "Enable this option to omit the custom Change filament G-code only at the " @@ -9997,6 +10042,11 @@ msgid "" "printing, where we use M600/PAUSE to trigger the manual filament change " "action." msgstr "" +"Sadece baskının başında özel Filament Değiştirme G-kodu'nu atlamak için " +"bu seçeneği etkinleştirin. Aracı değiştirme komutu (örneğin, T0), baskının " +"tamamı boyunca atlanacaktır. Bu, manuel çoklu malzeme baskısı için " +"kullanışlıdır, burada manuel filament değişim eylemini tetiklemek için M600/PAUSE " +"kullanırız." msgid "Purge in prime tower" msgstr "Prime Tower'da temizlik" @@ -10017,7 +10067,7 @@ msgid "" "with the print." msgstr "" "Etkinleştirilirse, silme kulesi araç değişimi olmayan katmanlarda " -"yazdırılmayacaktır. Araç değişimi olan katmanlarda, ekstrüder silme kulesini " +"yazdırılmayacaktır. Araç değişimi olan katmanlarda, ekstruder silme kulesini " "yazdırmak için aşağı doğru hareket edecektir. Baskı ile çarpışma olmamasını " "sağlamak kullanıcının sorumluluğundadır." @@ -10028,7 +10078,7 @@ msgid "" "If enabled, all printing extruders will be primed at the front edge of the " "print bed at the start of the print." msgstr "" -"Etkinleştirilirse, tüm baskı ekstrüderleri baskının başlangıcında baskı " +"Etkinleştirilirse, tüm baskı ekstruderleri baskının başlangıcında baskı " "yatağının ön kenarında temizlenecektir." msgid "Slice gap closing radius" @@ -10063,7 +10113,7 @@ msgid "Close holes" msgstr "Delikleri kapat" msgid "Z offset" -msgstr "" +msgstr "Z ofseti" msgid "" "This value will be added (or subtracted) from all the Z coordinates in the " @@ -10071,6 +10121,10 @@ msgid "" "example, if your endstop zero actually leaves the nozzle 0.3mm far from the " "print bed, set this to -0.3 (or fix your endstop)." msgstr "" +"Bu değer, çıkış G-kodu içindeki tüm Z koordinatlarına eklenir (veya çıkarılır)." +"Bu, kötü Z endstop konumunu telafi etmek için kullanılır: örneğin, endstop " +"sıfır noktanız aslında nozulu baskı tablasından 0.3mm uzakta bırakıyorsa, " +"bu değeri -0.3 olarak ayarlayın (veya endstop'unuzu düzeltin)." msgid "Enable support" msgstr "Desteği etkinleştir" @@ -10083,7 +10137,7 @@ msgid "" "normal(manual) or tree(manual) is selected, only support enforcers are " "generated" msgstr "" -"desteği otomatik olarak oluşturmak için normal(otomatik) ve ağaç(otomatik) " +"Desteği otomatik olarak oluşturmak için normal(otomatik) ve ağaç(otomatik) " "kullanılır. Normal(manuel) veya ağaç(manuel) seçilirse yalnızca destek " "uygulayıcıları oluşturulur" @@ -10153,7 +10207,7 @@ msgid "" "filament for support and current filament is used" msgstr "" "Destek tabanını ve salı yazdırmak için filament. \"Varsayılan\", destek için " -"belirli bir filamanın olmadığı ve mevcut filamanın kullanıldığı anlamına " +"belirli bir filamentin olmadığı ve mevcut filamentin kullanıldığı anlamına " "gelir" msgid "" @@ -10180,7 +10234,7 @@ msgid "" "for support interface and current filament is used" msgstr "" "Filament baskı desteği arayüzü. \"Varsayılan\", destek arayüzü için özel bir " -"filamanın olmadığı ve mevcut filamanın kullanıldığı anlamına gelir" +"filamentin olmadığı ve mevcut filamentin kullanıldığı anlamına gelir" msgid "Top interface layers" msgstr "Üst arayüz katmanları" @@ -10228,7 +10282,7 @@ msgid "" msgstr "" "Destek arayüzünün çizgi deseni. Çözünmeyen destek arayüzü için varsayılan " "model Doğrusaldır, çözünebilir destek arayüzü için varsayılan model ise " -"Eşmerkezlidir" +"eşmerkezlidir" msgid "Rectilinear Interlaced" msgstr "Doğrusal Taramalı" @@ -10257,6 +10311,13 @@ msgid "" "style will create similar structure to normal support under large flat " "overhangs." msgstr "" +"Destek stil ve şekli. Normal destek için, destekleri düzenli bir ızgara " +"içine projelendirmek daha stabil destekler oluşturacaktır (varsayılan), " +"aynı zamanda sıkı destek kuleleri malzeme tasarrufu sağlar ve nesne üzerindeki izleri azaltır.\n" +"Ağaç destek için, ince ve organik tarz, dalları daha etkili bir şekilde birleştirir ve " +"büyük düz çıkıntılarda normal destekle benzer bir yapı oluştururken birçok malzeme " +"tasarrufu sağlar (varsayılan organik tarz). Hybrid stil, büyük düz çıkıntıların altında normal " +"destekle benzer bir yapı oluşturacaktır." msgid "Snug" msgstr "Snug" @@ -10427,13 +10488,16 @@ msgstr "" "eklenmeyeceğini belirtir" msgid "Activate temperature control" -msgstr "" +msgstr "Sıcaklık kontrolünü etkinleştirin" msgid "" "Enable this option for chamber temperature control. An M191 command will be " "added before \"machine_start_gcode\"\n" "G-code commands: M141/M191 S(0-255)" msgstr "" +"Hazne sıcaklığı kontrolü için bu seçeneği etkinleştirin. Önce bir M191 komutu " +"eklenecek \"machine_start_gcode\"\n" +"G-code komut: M141/M191 S(0-255)" msgid "Chamber temperature" msgstr "Bölme sıcaklığı" @@ -10476,7 +10540,7 @@ msgstr "" "filament değiştirildiğinde eklenir" msgid "This gcode is inserted when the extrusion role is changed" -msgstr "" +msgstr "Bu gcode, ekstrüzyon rolü değiştirildiğinde eklenir" msgid "" "Line width for top surfaces. If expressed as a %, it will be computed over " @@ -10568,7 +10632,7 @@ msgid "Prime volume" msgstr "Ana hacim" msgid "The volume of material to prime extruder on tower." -msgstr "Kule üzerindeki ana ekstrüder malzeme hacmi." +msgstr "Kule üzerindeki ana ekstruder malzeme hacmi." msgid "Width" msgstr "Genişlik" @@ -10599,13 +10663,13 @@ msgid "Spacing of purge lines on the wipe tower." msgstr "Silme kulesindeki boşaltma hatlarının aralığı." msgid "Wipe tower extruder" -msgstr "Silme kulesi ekstrüderi" +msgstr "Silme kulesi ekstruderi" msgid "" "The extruder to use when printing perimeter of the wipe tower. Set to 0 to " "use the one that is available (non-soluble would be preferred)." msgstr "" -"Silme kulesinin çevresini yazdırırken kullanılacak ekstrüder. Mevcut olanı " +"Silme kulesinin çevresini yazdırırken kullanılacak ekstruder. Mevcut olanı " "kullanmak için 0 olarak ayarlayın (çözünmeyen tercih edilir)." msgid "Purging volumes - load/unload volumes" @@ -10688,7 +10752,7 @@ msgstr "" "kullanılır" msgid "Convert holes to polyholes" -msgstr "" +msgstr "Delikleri çokgen deliklere dönüştür" msgid "" "Search for almost-circular holes that span more than one layer and convert " @@ -10698,7 +10762,7 @@ msgid "" msgstr "" msgid "Polyhole detection margin" -msgstr "" +msgstr "Çokgen delik tespiti marjı" #, c-format, boost-format msgid "" @@ -10710,10 +10774,10 @@ msgid "" msgstr "" msgid "Polyhole twist" -msgstr "" +msgstr "Çokgen delik eğrisi" msgid "Rotate the polyhole every layer." -msgstr "" +msgstr "Çokgeni her katmanda döndürün." msgid "G-code thumbnails" msgstr "G-kodu küçük resimleri" @@ -10726,7 +10790,7 @@ msgstr "" "saklanacaktır: \"XxY, XxY, ...\"" msgid "Format of G-code thumbnails" -msgstr "" +msgstr "G kodu küçük resimlerinin formatı" msgid "" "Format of G-code thumbnails: PNG for best quality, JPG for smallest size, " @@ -10743,7 +10807,7 @@ msgid "" "BambuLab printers. Default is checked" msgstr "" "\"label_objects\" seçeneği kullanılırken göreceli ekstrüzyon önerilir. Bazı " -"ekstrüderler bu seçeneğin işareti kaldırıldığında (mutlak ekstrüzyon modu) " +"ekstruderler bu seçeneğin işareti kaldırıldığında (mutlak ekstrüzyon modu) " "daha iyi çalışır. Temizleme kulesi yalnızca göreceli modla uyumludur. " "BambuLab yazıcılarında her zaman etkindir. Varsayılan olarak işaretlendi" @@ -10932,7 +10996,7 @@ msgid "Load default filaments" msgstr "Varsayılan filamentleri yükle" msgid "Load first filament as default for those not loaded" -msgstr "Yüklenmeyenler için ilk filamanı varsayılan olarak yükleyin" +msgstr "Yüklenmeyenler için ilk filamenti varsayılan olarak yükleyin" msgid "Minimum save" msgstr "Minimum tasarruf" @@ -11413,7 +11477,7 @@ msgstr "" "\n" "Genellikle kalibrasyon gereksizdir. Yazdırma başlat menüsündeki \"akış " "dinamiği kalibrasyonu\" seçeneği işaretliyken tek renkli/malzeme baskısını " -"başlattığınızda, yazıcı eski yöntemi izleyecek, yazdırmadan önce filamanı " +"başlattığınızda, yazıcı eski yöntemi izleyecek, yazdırmadan önce filamenti " "kalibre edecektir; Çok renkli/malzeme baskısını başlattığınızda, yazıcı her " "filament değişiminde filament için varsayılan dengeleme parametresini " "kullanacaktır ve bu çoğu durumda iyi bir sonuç verecektir.\n" @@ -11569,7 +11633,7 @@ msgid "Please find the best line on your plate" msgstr "Lütfen plakadaki en iyi çizgiyi bulun" msgid "Please find the cornor with perfect degree of extrusion" -msgstr "" +msgstr "Lütfen mükemmel ekstrüzyon derecesine sahip köşeyi bulun" msgid "Input Value" msgstr "Girdi değeri" @@ -11808,7 +11872,7 @@ msgid "Delete input" msgstr "Girişi sil" msgid "Send G-Code to printer host" -msgstr "" +msgstr "G Kodunu yazıcı ana bilgisayarına gönder" msgid "Upload to Printer Host with the following filename:" msgstr "Yazıcıya aşağıdaki dosya adıyla yükleyin:" @@ -11861,7 +11925,7 @@ msgid "Cancelling" msgstr "İptal Ediliyor" msgid "Error uploading to print host" -msgstr "" +msgstr "Yazdırma ana bilgisayarına yükleme hatası" msgid "PA Calibration" msgstr "PA Kalibrasyonu" @@ -11946,6 +12010,10 @@ msgid "" "End temp: >= 170\n" "Start temp > End temp + 5)" msgstr "" +"Lütfen geçerli değerleri girin:\n" +"Başlangıç sıcaklığı: <= 350\n" +"Bitiş sıcaklığı: >= 170\n" +"Başlangıç sıcaklığı > Bitiş sıcaklığı + 5)" msgid "Max volumetric speed test" msgstr "Maksimum hacimsel hız testi" @@ -11965,6 +12033,10 @@ msgid "" "step >= 0\n" "end > start + step)" msgstr "" +"Lütfen geçerli değerleri girin:\n" +"başlangıç > 0 \n" +"adım >= 0\n" +"bitiş > başlangıç + adım)" msgid "VFA test" msgstr "VFA testi" @@ -11981,6 +12053,10 @@ msgid "" "step >= 0\n" "end > start + step)" msgstr "" +"Lütfen geçerli değerleri girin:\n" +"başlangıç > 10 \n" +"adım >= 0\n" +"bitiş > başlangıç + adım)" msgid "Start retraction length: " msgstr "Geri çekme uzunluğu başlangıcı: " @@ -12272,7 +12348,7 @@ msgid "" "the best results." msgstr "" "İpek Filament Baskı\n" -"İpek filamanın başarılı bir şekilde basılabilmesi için özel dikkat " +"İpek filamentin başarılı bir şekilde basılabilmesi için özel dikkat " "gösterilmesi gerektiğini biliyor muydunuz? En iyi sonuçlar için her zaman " "daha yüksek sıcaklık ve daha düşük hız önerilir." @@ -12311,7 +12387,7 @@ msgid "" "support/objects/infill during filament change?" msgstr "" "Desteğe/nesnelere/dolguya hizalayın\n" -"Filament değişimi sırasında, boşa harcanan filamanı desteğe/nesnelere/" +"Filament değişimi sırasında, boşa harcanan filamenti desteğe/nesnelere/" "dolguya yıkayarak kurtarabileceğinizi biliyor muydunuz?" #: resources/data/hints.ini: [hint:Improve strength] @@ -12391,7 +12467,7 @@ msgstr "" #~ "The bed temperature exceeds filament's vitrification temperature. Please " #~ "open the front door of printer before printing to avoid nozzle clog." #~ msgstr "" -#~ "Yatak sıcaklığı filamanın vitrifikasyon sıcaklığını aşıyor. Püskürtme " +#~ "Yatak sıcaklığı filamentin vitrifikasyon sıcaklığını aşıyor. Püskürtme " #~ "ucunun tıkanmasını önlemek için lütfen yazdırmadan önce yazıcının ön " #~ "kapısını açın." diff --git a/resources/images/copy_menu.svg b/resources/images/copy_menu.svg new file mode 100644 index 0000000000..23e0bfeb2a --- /dev/null +++ b/resources/images/copy_menu.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/copy_menu_dark.svg b/resources/images/copy_menu_dark.svg new file mode 100644 index 0000000000..eaee113a1b --- /dev/null +++ b/resources/images/copy_menu_dark.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/toolbar_measure.svg b/resources/images/toolbar_measure.svg new file mode 100644 index 0000000000..1606b5ee6f --- /dev/null +++ b/resources/images/toolbar_measure.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/toolbar_measure_dark.svg b/resources/images/toolbar_measure_dark.svg new file mode 100644 index 0000000000..a273e75347 --- /dev/null +++ b/resources/images/toolbar_measure_dark.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/resources/profiles/Anker.json b/resources/profiles/Anker.json index 38904a3bc6..03f208a42b 100644 --- a/resources/profiles/Anker.json +++ b/resources/profiles/Anker.json @@ -1,6 +1,6 @@ { "name": "Anker", - "version": "01.06.04.00", + "version": "01.08.00.00", "force_update": "0", "description": "Anker configurations", "machine_model_list": [ diff --git a/resources/profiles/Anycubic.json b/resources/profiles/Anycubic.json index 410576d4e4..cd68215067 100644 --- a/resources/profiles/Anycubic.json +++ b/resources/profiles/Anycubic.json @@ -1,6 +1,6 @@ { "name": "Anycubic", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Anycubic configurations", "machine_model_list": [ diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic 4MaxPro2.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic 4MaxPro2.json index 6babe744c2..c7b30bc5f4 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic 4MaxPro2.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic 4MaxPro2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Chiron.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Chiron.json index 543e0d2d95..e516880a08 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Chiron.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Chiron.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraMax.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraMax.json index ea9ef86693..73db772a2a 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraMax.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraPlus.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraPlus.json index 7cfd4daf10..f236f609e0 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraPlus.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic KobraPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Vyper.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Vyper.json index f50094c381..ca65287273 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Vyper.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic Vyper.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic i3MegaS.json b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic i3MegaS.json index 026d45d1ca..ffa79dfcc0 100644 --- a/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic i3MegaS.json +++ b/resources/profiles/Anycubic/process/0.15mm Optimal @Anycubic i3MegaS.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic 4MaxPro2.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic 4MaxPro2.json index ca160587c6..3eec452725 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic 4MaxPro2.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic 4MaxPro2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Chiron.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Chiron.json index d0b6f45e86..99a1501abb 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Chiron.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Chiron.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraMax.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraMax.json index 8efba928a2..097e6ae25c 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraMax.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraPlus.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraPlus.json index 9207419ad3..249aae8b65 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraPlus.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic KobraPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Vyper.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Vyper.json index f8b80f8a9a..f1c5e98f04 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Vyper.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic Vyper.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic i3MegaS.json b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic i3MegaS.json index 4dfe316fa8..7e8f2256da 100644 --- a/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic i3MegaS.json +++ b/resources/profiles/Anycubic/process/0.20mm Standard @Anycubic i3MegaS.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic 4MaxPro2.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic 4MaxPro2.json index 7c96080441..c6fd6c3b4f 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic 4MaxPro2.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic 4MaxPro2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Chiron.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Chiron.json index c7dba4627a..496528f2c0 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Chiron.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Chiron.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraMax.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraMax.json index 5dcc5385f7..605f81e5c3 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraMax.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraPlus.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraPlus.json index 1e55e6d0c4..f42c73f1d9 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraPlus.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic KobraPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Vyper.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Vyper.json index 9cbf32870c..f3fe2d9587 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Vyper.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic Vyper.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic i3MegaS.json b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic i3MegaS.json index c0ea8cc91e..1f2f55dfcb 100644 --- a/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic i3MegaS.json +++ b/resources/profiles/Anycubic/process/0.30mm Draft @Anycubic i3MegaS.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Anycubic/process/fdm_process_common.json b/resources/profiles/Anycubic/process/fdm_process_common.json index faf2d9af79..30291bf626 100644 --- a/resources/profiles/Anycubic/process/fdm_process_common.json +++ b/resources/profiles/Anycubic/process/fdm_process_common.json @@ -41,7 +41,7 @@ "ironing_speed": "30", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Artillery.json b/resources/profiles/Artillery.json index 20c710b1cf..23da2f209e 100644 --- a/resources/profiles/Artillery.json +++ b/resources/profiles/Artillery.json @@ -1,6 +1,6 @@ { "name": "Artillery", - "version": "01.06.04.00", + "version": "01.08.00.00", "force_update": "0", "description": "Artillery configurations", "machine_model_list": [ diff --git a/resources/profiles/Artillery/process/0.15mm Optimal @Artillery Genius.json b/resources/profiles/Artillery/process/0.15mm Optimal @Artillery Genius.json index a19ff60848..02f19906a6 100644 --- a/resources/profiles/Artillery/process/0.15mm Optimal @Artillery Genius.json +++ b/resources/profiles/Artillery/process/0.15mm Optimal @Artillery Genius.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.16mm Optimal @Artillery Hornet.json b/resources/profiles/Artillery/process/0.16mm Optimal @Artillery Hornet.json index 2a2d537d85..90b3717bc7 100644 --- a/resources/profiles/Artillery/process/0.16mm Optimal @Artillery Hornet.json +++ b/resources/profiles/Artillery/process/0.16mm Optimal @Artillery Hornet.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.16mm Optimal @Artillery X1.json b/resources/profiles/Artillery/process/0.16mm Optimal @Artillery X1.json index 2ec2ebb93b..23b5e6605e 100644 --- a/resources/profiles/Artillery/process/0.16mm Optimal @Artillery X1.json +++ b/resources/profiles/Artillery/process/0.16mm Optimal @Artillery X1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.20mm Standard @Artillery Genius.json b/resources/profiles/Artillery/process/0.20mm Standard @Artillery Genius.json index 410b266154..03d0e59ef3 100644 --- a/resources/profiles/Artillery/process/0.20mm Standard @Artillery Genius.json +++ b/resources/profiles/Artillery/process/0.20mm Standard @Artillery Genius.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.20mm Standard @Artillery Hornet.json b/resources/profiles/Artillery/process/0.20mm Standard @Artillery Hornet.json index 6188d382ef..24899e1bd9 100644 --- a/resources/profiles/Artillery/process/0.20mm Standard @Artillery Hornet.json +++ b/resources/profiles/Artillery/process/0.20mm Standard @Artillery Hornet.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.20mm Standard @Artillery X1.json b/resources/profiles/Artillery/process/0.20mm Standard @Artillery X1.json index ed223a5f0a..ed6853df93 100644 --- a/resources/profiles/Artillery/process/0.20mm Standard @Artillery X1.json +++ b/resources/profiles/Artillery/process/0.20mm Standard @Artillery X1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.20mm Standard @Artillery X2.json b/resources/profiles/Artillery/process/0.20mm Standard @Artillery X2.json index 05e7bdb481..327afad6f7 100644 --- a/resources/profiles/Artillery/process/0.20mm Standard @Artillery X2.json +++ b/resources/profiles/Artillery/process/0.20mm Standard @Artillery X2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.24mm Draft @Artillery Hornet.json b/resources/profiles/Artillery/process/0.24mm Draft @Artillery Hornet.json index b2977c6260..d3fb6cc4fd 100644 --- a/resources/profiles/Artillery/process/0.24mm Draft @Artillery Hornet.json +++ b/resources/profiles/Artillery/process/0.24mm Draft @Artillery Hornet.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.24mm Draft @Artillery X1.json b/resources/profiles/Artillery/process/0.24mm Draft @Artillery X1.json index edbb4817fd..06b10e836a 100644 --- a/resources/profiles/Artillery/process/0.24mm Draft @Artillery X1.json +++ b/resources/profiles/Artillery/process/0.24mm Draft @Artillery X1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/0.25mm Draft @Artillery Genius.json b/resources/profiles/Artillery/process/0.25mm Draft @Artillery Genius.json index bfbf62199d..d5e0f991dd 100644 --- a/resources/profiles/Artillery/process/0.25mm Draft @Artillery Genius.json +++ b/resources/profiles/Artillery/process/0.25mm Draft @Artillery Genius.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Artillery/process/fdm_process_common.json b/resources/profiles/Artillery/process/fdm_process_common.json index faf2d9af79..30291bf626 100644 --- a/resources/profiles/Artillery/process/fdm_process_common.json +++ b/resources/profiles/Artillery/process/fdm_process_common.json @@ -41,7 +41,7 @@ "ironing_speed": "30", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json index 50cb96327e..2caae25cd1 100644 --- a/resources/profiles/BBL.json +++ b/resources/profiles/BBL.json @@ -1,7 +1,7 @@ { "name": "Bambulab", "url": "http://www.bambulab.com/Parameters/vendor/BBL.json", - "version": "01.07.00.29", + "version": "01.07.00.30", "force_update": "0", "description": "the initial version of BBL configurations", "machine_model_list": [ diff --git a/resources/profiles/BBL/filament/fdm_filament_abs.json b/resources/profiles/BBL/filament/fdm_filament_abs.json index 461dd5e610..74eb871518 100644 --- a/resources/profiles/BBL/filament/fdm_filament_abs.json +++ b/resources/profiles/BBL/filament/fdm_filament_abs.json @@ -5,7 +5,7 @@ "from": "system", "instantiation": "false", "activate_air_filtration": [ - "1" + "0" ], "cool_plate_temp": [ "0" diff --git a/resources/profiles/BBL/filament/fdm_filament_asa.json b/resources/profiles/BBL/filament/fdm_filament_asa.json index ac7a9294c1..a0da767a75 100644 --- a/resources/profiles/BBL/filament/fdm_filament_asa.json +++ b/resources/profiles/BBL/filament/fdm_filament_asa.json @@ -5,7 +5,7 @@ "from": "system", "instantiation": "false", "activate_air_filtration": [ - "1" + "0" ], "cool_plate_temp": [ "0" diff --git a/resources/profiles/BBL/process/fdm_process_bbl_common.json b/resources/profiles/BBL/process/fdm_process_bbl_common.json index 223e2e9eb7..526773face 100644 --- a/resources/profiles/BBL/process/fdm_process_bbl_common.json +++ b/resources/profiles/BBL/process/fdm_process_bbl_common.json @@ -29,7 +29,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/BIQU.json b/resources/profiles/BIQU.json index f3620e9284..a53d4e315b 100644 --- a/resources/profiles/BIQU.json +++ b/resources/profiles/BIQU.json @@ -1,6 +1,6 @@ { "name": "BIQU", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "BIQU configurations", "machine_model_list": [ diff --git a/resources/profiles/BIQU/process/fdm_process_biqu_common.json b/resources/profiles/BIQU/process/fdm_process_biqu_common.json index f6f264c868..0375a6ced1 100644 --- a/resources/profiles/BIQU/process/fdm_process_biqu_common.json +++ b/resources/profiles/BIQU/process/fdm_process_biqu_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/BIQU/process/fdm_process_hurakan_common.json b/resources/profiles/BIQU/process/fdm_process_hurakan_common.json index 2519589647..ac3fa6d8f5 100644 --- a/resources/profiles/BIQU/process/fdm_process_hurakan_common.json +++ b/resources/profiles/BIQU/process/fdm_process_hurakan_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Comgrow.json b/resources/profiles/Comgrow.json index d412cfd668..dc337e7400 100644 --- a/resources/profiles/Comgrow.json +++ b/resources/profiles/Comgrow.json @@ -1,6 +1,6 @@ { "name": "Comgrow", - "version": "01.07.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Comgrow configurations", "machine_model_list": [ diff --git a/resources/profiles/Creality.json b/resources/profiles/Creality.json index e73663c666..7c9e40ea84 100644 --- a/resources/profiles/Creality.json +++ b/resources/profiles/Creality.json @@ -1,6 +1,6 @@ { "name": "Creality", - "version": "01.07.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Creality configurations", "machine_model_list": [ diff --git a/resources/profiles/Creality/machine/Creality K1 Max.json b/resources/profiles/Creality/machine/Creality K1 Max.json index 01a864bd4c..e1d1d5fcd7 100644 --- a/resources/profiles/Creality/machine/Creality K1 Max.json +++ b/resources/profiles/Creality/machine/Creality K1 Max.json @@ -8,5 +8,5 @@ "bed_model": "creality_k1max_buildplate_model.stl", "bed_texture": "creality_k1max_buildplate_texture.png", "hotend_model": "", - "default_materials": "Creality Generic PLA @0.4 nozzle;Creality HF Generic PLA @0.4 nozzle;Creality HF Generic Speed PLA @0.4 nozzle;Creality Generic PETG @0.4 nozzle;Creality Generic TPU @0.4 nozzle;Creality Generic ABS @0.4 nozzle;Creality Generic PLA @0.6 nozzle;Creality HF Generic PLA @0.6 nozzle;Creality HF Generic Speed PLA @0.6 nozzle;Creality Generic PETG @0.6 nozzle;Creality Generic TPU @0.6 nozzle;Creality Generic ABS @0.6 nozzle;Creality Generic PLA @0.8 nozzle;Creality HF Generic PLA @0.8 nozzle;Creality HF Generic Speed PLA @0.8 nozzle;Creality Generic PETG @0.8 nozzle;Creality Generic TPU @0.8 nozzle;Creality Generic ABS @0.8 nozzle" + "default_materials": "Creality Generic PLA;Creality HF Generic PLA;Creality HF Generic Speed PLA;Creality Generic PETG;Creality Generic TPU;Creality Generic ABS" } diff --git a/resources/profiles/Creality/machine/Creality K1.json b/resources/profiles/Creality/machine/Creality K1.json index 413022fcdf..bc76e3bf7c 100644 --- a/resources/profiles/Creality/machine/Creality K1.json +++ b/resources/profiles/Creality/machine/Creality K1.json @@ -8,5 +8,5 @@ "bed_model": "creality_k1_buildplate_model.stl", "bed_texture": "creality_k1_buildplate_texture.png", "hotend_model": "", - "default_materials": "Creality Generic PLA @0.4 nozzle;Creality HF Generic PLA @0.4 nozzle;Creality HF Generic Speed PLA @0.4 nozzle;Creality Generic PETG @0.4 nozzle;Creality Generic TPU @0.4 nozzle;Creality Generic ABS @0.4 nozzle;Creality Generic PLA @0.6 nozzle;Creality HF Generic PLA @0.6 nozzle;Creality HF Generic Speed PLA @0.6 nozzle;Creality Generic PETG @0.6 nozzle;Creality Generic TPU @0.6 nozzle;Creality Generic ABS @0.6 nozzle;Creality Generic PLA @0.8 nozzle;Creality HF Generic PLA @0.8 nozzle;Creality HF Generic Speed PLA @0.8 nozzle;Creality Generic PETG @0.8 nozzle;Creality Generic TPU @0.8 nozzle;Creality Generic ABS @0.8 nozzle" + "default_materials": "Creality Generic PLA;Creality HF Generic PLA;Creality HF Generic Speed PLA;Creality Generic PETG;Creality Generic TPU;Creality Generic ABS" } diff --git a/resources/profiles/Creality/process/0.12mm Fine @Creality CR10Max.json b/resources/profiles/Creality/process/0.12mm Fine @Creality CR10Max.json index 1c3cefe233..86937ca76f 100644 --- a/resources/profiles/Creality/process/0.12mm Fine @Creality CR10Max.json +++ b/resources/profiles/Creality/process/0.12mm Fine @Creality CR10Max.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.12", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.12mm Fine @Creality Ender3V2.json b/resources/profiles/Creality/process/0.12mm Fine @Creality Ender3V2.json index e4fff81299..eac84c4088 100644 --- a/resources/profiles/Creality/process/0.12mm Fine @Creality Ender3V2.json +++ b/resources/profiles/Creality/process/0.12mm Fine @Creality Ender3V2.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.12", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.12mm Fine @Creality Ender5Pro (2019).json b/resources/profiles/Creality/process/0.12mm Fine @Creality Ender5Pro (2019).json index 3613ada505..95e24872c8 100644 --- a/resources/profiles/Creality/process/0.12mm Fine @Creality Ender5Pro (2019).json +++ b/resources/profiles/Creality/process/0.12mm Fine @Creality Ender5Pro (2019).json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.12", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.15mm Optimal @Creality CR10Max.json b/resources/profiles/Creality/process/0.15mm Optimal @Creality CR10Max.json index 3d0db3caa7..eb649fb1f1 100644 --- a/resources/profiles/Creality/process/0.15mm Optimal @Creality CR10Max.json +++ b/resources/profiles/Creality/process/0.15mm Optimal @Creality CR10Max.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.15", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender3V2.json b/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender3V2.json index 6856368697..997f6face1 100644 --- a/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender3V2.json +++ b/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender3V2.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.15", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender5Pro (2019).json b/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender5Pro (2019).json index c67ceeda6a..9404f9d2f8 100644 --- a/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender5Pro (2019).json +++ b/resources/profiles/Creality/process/0.15mm Optimal @Creality Ender5Pro (2019).json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.15", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality CR10V2.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality CR10V2.json index bf21389e53..0ded3d9508 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality CR10V2.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality CR10V2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1.json index 13a985f13c..2d7a02e45e 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1Pro.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1Pro.json index 0a1336b9c3..6897175b64 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1Pro.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender3S1Pro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5.json index fe5711c03d..5600ad9174 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5Plus.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5Plus.json index b6ab429ba3..78c523f829 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5Plus.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5Plus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S.json index b26cb9d89f..86044fd774 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S1.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S1.json index 9a3b288d7c..64fdc16a1f 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S1.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender5S1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender6.json b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender6.json index 39c8022f19..6586f39240 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender6.json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality Ender6.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality K1 (0.4 nozzle).json b/resources/profiles/Creality/process/0.16mm Optimal @Creality K1 (0.4 nozzle).json index d0db47b346..163f47fdc8 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality K1 (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality K1 (0.4 nozzle).json @@ -51,7 +51,7 @@ "ironing_type": "no ironing", "layer_height": "0.16", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.16mm Optimal @Creality K1Max (0.4 nozzle).json b/resources/profiles/Creality/process/0.16mm Optimal @Creality K1Max (0.4 nozzle).json index 30ac1cc624..3001612377 100644 --- a/resources/profiles/Creality/process/0.16mm Optimal @Creality K1Max (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.16mm Optimal @Creality K1Max (0.4 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.16", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality CR10Max.json b/resources/profiles/Creality/process/0.20mm Standard @Creality CR10Max.json index a2ca2e66c9..9913f7f5b7 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality CR10Max.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality CR10Max.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality CR10V2.json b/resources/profiles/Creality/process/0.20mm Standard @Creality CR10V2.json index 077eeee6ad..a6cbe7ac1a 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality CR10V2.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality CR10V2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3.json index f63b773e5b..94574083f0 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1.json index 9a692a5f82..a3d25a670c 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1Pro.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1Pro.json index df406a4ddd..4743cb6256 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1Pro.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3S1Pro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3V2.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3V2.json index b7bcff8bbb..ec55c75391 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3V2.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender3V2.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5.json index cdf7af7fc1..b8ab9727f6 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Plus.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Plus.json index 4db9d64c9a..c9518d4655 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Plus.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Plus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Pro (2019).json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Pro (2019).json index 48d4d98e51..04b6b36d34 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Pro (2019).json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5Pro (2019).json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S.json index 1ed5693f09..63815b0e6e 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S1.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S1.json index 801be3e549..608e67142b 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S1.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender5S1.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender6.json b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender6.json index ce6a673302..ae98760d8a 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality Ender6.json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality Ender6.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality K1 (0.4 nozzle).json b/resources/profiles/Creality/process/0.20mm Standard @Creality K1 (0.4 nozzle).json index 378a1250aa..1e115ba9e4 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality K1 (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality K1 (0.4 nozzle).json @@ -51,7 +51,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality K1Max (0.4 nozzle).json b/resources/profiles/Creality/process/0.20mm Standard @Creality K1Max (0.4 nozzle).json index acb91b51bf..725adc69ac 100644 --- a/resources/profiles/Creality/process/0.20mm Standard @Creality K1Max (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality K1Max (0.4 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality CR10Max.json b/resources/profiles/Creality/process/0.24mm Draft @Creality CR10Max.json index 209ded8211..59fe41c01d 100644 --- a/resources/profiles/Creality/process/0.24mm Draft @Creality CR10Max.json +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality CR10Max.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality Ender3V2.json b/resources/profiles/Creality/process/0.24mm Draft @Creality Ender3V2.json index b2db3fb562..f9727bbf9c 100644 --- a/resources/profiles/Creality/process/0.24mm Draft @Creality Ender3V2.json +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality Ender3V2.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality Ender5Pro (2019).json b/resources/profiles/Creality/process/0.24mm Draft @Creality Ender5Pro (2019).json index 7354faf490..d6df736aba 100644 --- a/resources/profiles/Creality/process/0.24mm Draft @Creality Ender5Pro (2019).json +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality Ender5Pro (2019).json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality K1 (0.4 nozzle).json b/resources/profiles/Creality/process/0.24mm Draft @Creality K1 (0.4 nozzle).json index 2e497198ab..ad233c1310 100644 --- a/resources/profiles/Creality/process/0.24mm Draft @Creality K1 (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality K1 (0.4 nozzle).json @@ -49,9 +49,9 @@ "ironing_spacing": "0.15", "ironing_speed": "100", "ironing_type": "no ironing", - "layer_height": "0.2", + "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality K1Max (0.4 nozzle).json b/resources/profiles/Creality/process/0.24mm Draft @Creality K1Max (0.4 nozzle).json index 7e7d68f574..7ea7a1846a 100644 --- a/resources/profiles/Creality/process/0.24mm Draft @Creality K1Max (0.4 nozzle).json +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality K1Max (0.4 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.24mm Optimal @Creality K1 (0.6 nozzle).json b/resources/profiles/Creality/process/0.24mm Optimal @Creality K1 (0.6 nozzle).json index 14ddd750eb..558d2786d6 100644 --- a/resources/profiles/Creality/process/0.24mm Optimal @Creality K1 (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.24mm Optimal @Creality K1 (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.24mm Optimal @Creality K1Max (0.6 nozzle).json b/resources/profiles/Creality/process/0.24mm Optimal @Creality K1Max (0.6 nozzle).json index 7ac9cbe9f1..5bc47cd737 100644 --- a/resources/profiles/Creality/process/0.24mm Optimal @Creality K1Max (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.24mm Optimal @Creality K1Max (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.24", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.30mm Standard @Creality K1 (0.6 nozzle).json b/resources/profiles/Creality/process/0.30mm Standard @Creality K1 (0.6 nozzle).json index 9c47f7d942..fb8ab24e95 100644 --- a/resources/profiles/Creality/process/0.30mm Standard @Creality K1 (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.30mm Standard @Creality K1 (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.3", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.30mm Standard @Creality K1Max (0.6 nozzle).json b/resources/profiles/Creality/process/0.30mm Standard @Creality K1Max (0.6 nozzle).json index ead330c693..e19704fc2a 100644 --- a/resources/profiles/Creality/process/0.30mm Standard @Creality K1Max (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.30mm Standard @Creality K1Max (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.3", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.32mm Optimal @Creality K1 (0.8 nozzle).json b/resources/profiles/Creality/process/0.32mm Optimal @Creality K1 (0.8 nozzle).json index 4349548d87..7e562392d5 100644 --- a/resources/profiles/Creality/process/0.32mm Optimal @Creality K1 (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.32mm Optimal @Creality K1 (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.32", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.32mm Optimal @Creality K1Max (0.8 nozzle).json b/resources/profiles/Creality/process/0.32mm Optimal @Creality K1Max (0.8 nozzle).json index 21eac2347e..b449ebc101 100644 --- a/resources/profiles/Creality/process/0.32mm Optimal @Creality K1Max (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.32mm Optimal @Creality K1Max (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.32", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.36mm Draft @Creality K1 (0.6 nozzle).json b/resources/profiles/Creality/process/0.36mm Draft @Creality K1 (0.6 nozzle).json index 6b54d3c3e6..9d7d3d4ac5 100644 --- a/resources/profiles/Creality/process/0.36mm Draft @Creality K1 (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.36mm Draft @Creality K1 (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.36", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.36mm Draft @Creality K1Max (0.6 nozzle).json b/resources/profiles/Creality/process/0.36mm Draft @Creality K1Max (0.6 nozzle).json index cf796e77cf..63c7107e5b 100644 --- a/resources/profiles/Creality/process/0.36mm Draft @Creality K1Max (0.6 nozzle).json +++ b/resources/profiles/Creality/process/0.36mm Draft @Creality K1Max (0.6 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.36", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.40mm Standard @Creality K1 (0.8 nozzle).json b/resources/profiles/Creality/process/0.40mm Standard @Creality K1 (0.8 nozzle).json index a4c4cef212..b72a91f6bf 100644 --- a/resources/profiles/Creality/process/0.40mm Standard @Creality K1 (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.40mm Standard @Creality K1 (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.4", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.40mm Standard @Creality K1Max (0.8 nozzle).json b/resources/profiles/Creality/process/0.40mm Standard @Creality K1Max (0.8 nozzle).json index 73ffc4dcd5..0afc0a0363 100644 --- a/resources/profiles/Creality/process/0.40mm Standard @Creality K1Max (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.40mm Standard @Creality K1Max (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.4", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.48mm Draft @Creality K1 (0.8 nozzle).json b/resources/profiles/Creality/process/0.48mm Draft @Creality K1 (0.8 nozzle).json index aa22d082e0..a51952a74d 100644 --- a/resources/profiles/Creality/process/0.48mm Draft @Creality K1 (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.48mm Draft @Creality K1 (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.48", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/0.48mm Draft @Creality K1Max (0.8 nozzle).json b/resources/profiles/Creality/process/0.48mm Draft @Creality K1Max (0.8 nozzle).json index 3f50fcec4c..6842de7ced 100644 --- a/resources/profiles/Creality/process/0.48mm Draft @Creality K1Max (0.8 nozzle).json +++ b/resources/profiles/Creality/process/0.48mm Draft @Creality K1Max (0.8 nozzle).json @@ -49,7 +49,7 @@ "ironing_type": "no ironing", "layer_height": "0.48", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Creality/process/fdm_process_creality_common.json b/resources/profiles/Creality/process/fdm_process_creality_common.json index 5d6c90e849..83c8322a28 100644 --- a/resources/profiles/Creality/process/fdm_process_creality_common.json +++ b/resources/profiles/Creality/process/fdm_process_creality_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Custom.json b/resources/profiles/Custom.json index cd749a0677..22e2f992bc 100644 --- a/resources/profiles/Custom.json +++ b/resources/profiles/Custom.json @@ -1,6 +1,6 @@ { "name": "Custom Printer", - "version": "01.06.01.00", + "version": "01.08.00.00", "force_update": "0", "description": "My configurations", "machine_model_list": [ diff --git a/resources/profiles/Custom/process/fdm_process_klipper_common.json b/resources/profiles/Custom/process/fdm_process_klipper_common.json index 111573307d..becc3fadd8 100644 --- a/resources/profiles/Custom/process/fdm_process_klipper_common.json +++ b/resources/profiles/Custom/process/fdm_process_klipper_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Custom/process/fdm_process_marlin_common.json b/resources/profiles/Custom/process/fdm_process_marlin_common.json index 4ca361a2ed..ad5002ab20 100644 --- a/resources/profiles/Custom/process/fdm_process_marlin_common.json +++ b/resources/profiles/Custom/process/fdm_process_marlin_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Elegoo.json b/resources/profiles/Elegoo.json index d5be4493d0..a5c0576488 100644 --- a/resources/profiles/Elegoo.json +++ b/resources/profiles/Elegoo.json @@ -1,6 +1,6 @@ { "name": "Elegoo", - "version": "01.07.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Elegoo configurations", "machine_model_list": [ diff --git a/resources/profiles/FLSun.json b/resources/profiles/FLSun.json index d6ad4084af..4f625b16ff 100644 --- a/resources/profiles/FLSun.json +++ b/resources/profiles/FLSun.json @@ -1,6 +1,6 @@ { "name": "FLSun", - "version": "01.06.04.00", + "version": "01.08.00.00", "force_update": "0", "description": "FLSun configurations", "machine_model_list": [ diff --git a/resources/profiles/FLSun/process/0.08mm Fine @FLSun Q5.json b/resources/profiles/FLSun/process/0.08mm Fine @FLSun Q5.json index e69f874987..b2f8c0a6ef 100644 --- a/resources/profiles/FLSun/process/0.08mm Fine @FLSun Q5.json +++ b/resources/profiles/FLSun/process/0.08mm Fine @FLSun Q5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.08mm Fine @FLSun QQSPro.json b/resources/profiles/FLSun/process/0.08mm Fine @FLSun QQSPro.json index fb305084fe..21d435028c 100644 --- a/resources/profiles/FLSun/process/0.08mm Fine @FLSun QQSPro.json +++ b/resources/profiles/FLSun/process/0.08mm Fine @FLSun QQSPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.16mm Optimal @FLSun Q5.json b/resources/profiles/FLSun/process/0.16mm Optimal @FLSun Q5.json index f428ad7c1d..d9aac91088 100644 --- a/resources/profiles/FLSun/process/0.16mm Optimal @FLSun Q5.json +++ b/resources/profiles/FLSun/process/0.16mm Optimal @FLSun Q5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.16mm Optimal @FLSun QQSPro.json b/resources/profiles/FLSun/process/0.16mm Optimal @FLSun QQSPro.json index 2782454eae..093584d733 100644 --- a/resources/profiles/FLSun/process/0.16mm Optimal @FLSun QQSPro.json +++ b/resources/profiles/FLSun/process/0.16mm Optimal @FLSun QQSPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.20mm Standard @FLSun Q5.json b/resources/profiles/FLSun/process/0.20mm Standard @FLSun Q5.json index 962385bf6c..b3e33c1928 100644 --- a/resources/profiles/FLSun/process/0.20mm Standard @FLSun Q5.json +++ b/resources/profiles/FLSun/process/0.20mm Standard @FLSun Q5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.20mm Standard @FLSun QQSPro.json b/resources/profiles/FLSun/process/0.20mm Standard @FLSun QQSPro.json index be85322b97..f4b46ac571 100644 --- a/resources/profiles/FLSun/process/0.20mm Standard @FLSun QQSPro.json +++ b/resources/profiles/FLSun/process/0.20mm Standard @FLSun QQSPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.24mm Draft @FLSun Q5.json b/resources/profiles/FLSun/process/0.24mm Draft @FLSun Q5.json index 101594de40..ca8b4bf23c 100644 --- a/resources/profiles/FLSun/process/0.24mm Draft @FLSun Q5.json +++ b/resources/profiles/FLSun/process/0.24mm Draft @FLSun Q5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.24mm Draft @FLSun QQSPro.json b/resources/profiles/FLSun/process/0.24mm Draft @FLSun QQSPro.json index 30fe88f7e6..f06ed8b702 100644 --- a/resources/profiles/FLSun/process/0.24mm Draft @FLSun QQSPro.json +++ b/resources/profiles/FLSun/process/0.24mm Draft @FLSun QQSPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun Q5.json b/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun Q5.json index a98622daec..f6a8572ee2 100644 --- a/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun Q5.json +++ b/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun Q5.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun QQSPro.json b/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun QQSPro.json index b745caa2fc..5b0c670c41 100644 --- a/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun QQSPro.json +++ b/resources/profiles/FLSun/process/0.30mm Extra Draft @FLSun QQSPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/FLSun/process/fdm_process_common.json b/resources/profiles/FLSun/process/fdm_process_common.json index ec9c890150..dbcc552bbd 100644 --- a/resources/profiles/FLSun/process/fdm_process_common.json +++ b/resources/profiles/FLSun/process/fdm_process_common.json @@ -41,7 +41,7 @@ "ironing_speed": "30", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Flashforge.json b/resources/profiles/Flashforge.json index 1bfca4fc71..f00dcda629 100644 --- a/resources/profiles/Flashforge.json +++ b/resources/profiles/Flashforge.json @@ -1,7 +1,7 @@ { "name": "Flashforge", "url": "", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Flashforge configurations", "machine_model_list": [ @@ -19,6 +19,18 @@ "name": "fdm_process_common", "sub_path": "process/fdm_process_common.json" }, + { + "name": "fdm_process_flashforge_common", + "sub_path": "process/fdm_process_flashforge_common.json" + }, + { + "name": "fdm_process_flashforge_0.20", + "sub_path": "process/fdm_process_flashforge_0.20.json" + }, + { + "name": "fdm_process_flashforge_0.30", + "sub_path": "process/fdm_process_flashforge_0.30.json" + }, { "name": "0.20mm Standard @Flashforge AD5M 0.4 Nozzle", "sub_path": "process/0.20mm Standard @Flashforge AD5M 0.4 Nozzle.json" diff --git a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.4 Nozzle.json b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.4 Nozzle.json index a8fc89ada6..a256bd2178 100644 --- a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.4 Nozzle.json +++ b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.4 Nozzle.json @@ -47,7 +47,7 @@ "machine_pause_gcode": "M25", "default_filament_profile": [ "Flashforge Generic PLA" ], "machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM104 S[nozzle_temperature_initial_layer]\nG1 Z5 F6000\nG1 E-1.5 F600\nG1 E12 F800\nG1 X85 Y110 Z0.25 F1200\nG1 X-110 E15 F2400\nG1 Y0 E4 F2400\nG1 X-109.6 F2400\nG1 Y110 E5 F2400\nG92 E0", - "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disble motors", + "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disable motors", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", "scan_first_layer": "0", diff --git a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.6 Nozzle.json b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.6 Nozzle.json index 8d4930ffe9..2c1c75aa70 100644 --- a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.6 Nozzle.json +++ b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M 0.6 Nozzle.json @@ -47,7 +47,7 @@ "machine_pause_gcode": "M25", "default_filament_profile": [ "Flashforge Generic PLA" ], "machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM104 S[nozzle_temperature_initial_layer]\nG1 Z5 F6000\nG1 E-1.5 F600\nG1 E12 F800\nG1 X85 Y110 Z0.25 F1200\nG1 X-110 E15 F2400\nG1 Y0 E4 F2400\nG1 X-109.6 F2400\nG1 Y110 E5 F2400\nG92 E0", - "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disble motors", + "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disable motors", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", "scan_first_layer": "0", diff --git a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.4 Nozzle.json b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.4 Nozzle.json index 42fae97d73..dded44af82 100644 --- a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.4 Nozzle.json +++ b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.4 Nozzle.json @@ -47,7 +47,7 @@ "machine_pause_gcode": "M25", "default_filament_profile": [ "Flashforge Generic PLA" ], "machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM104 S[nozzle_temperature_initial_layer]\nG1 Z5 F6000\nG1 E-1.5 F600\nG1 E12 F800\nG1 X85 Y110 Z0.25 F1200\nG1 X-110 E15 F2400\nG1 Y0 E4 F2400\nG1 X-109.6 F2400\nG1 Y110 E5 F2400\nG92 E0", - "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disble motors", + "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disable motors", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", "scan_first_layer": "0", diff --git a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.6 Nozzle.json b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.6 Nozzle.json index 70e7e483b5..60aba649ea 100644 --- a/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.6 Nozzle.json +++ b/resources/profiles/Flashforge/machine/Flashforge Adventurer 5M Pro 0.6 Nozzle.json @@ -47,7 +47,7 @@ "machine_pause_gcode": "M25", "default_filament_profile": [ "Flashforge Generic PLA" ], "machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM104 S[nozzle_temperature_initial_layer]\nG1 Z5 F6000\nG1 E-1.5 F600\nG1 E12 F800\nG1 X85 Y110 Z0.25 F1200\nG1 X-110 E15 F2400\nG1 Y0 E4 F2400\nG1 X-109.6 F2400\nG1 Y110 E5 F2400\nG92 E0", - "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disble motors", + "machine_end_gcode": "G1 E-3 F3600\nG0 X50 Y50 F30000\nG28\nM104 S0 ; turn off temperature\nM84 ; disable motors", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", "scan_first_layer": "0", diff --git a/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M 0.4 Nozzle.json b/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M 0.4 Nozzle.json index 37d091c649..662b265425 100644 --- a/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M 0.4 Nozzle.json +++ b/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M 0.4 Nozzle.json @@ -1,125 +1,11 @@ { "type": "process", - "setting_id": "GP004", "name": "0.20mm Standard @Flashforge AD5M 0.4 Nozzle", + "inherits": "fdm_process_flashforge_0.20", "from": "system", - "inherits": "fdm_process_common", + "setting_id": "GP001", "instantiation": "true", - "adaptive_layer_height": "0", - "reduce_crossing_wall": "0", - "max_travel_detour_distance": "0", - "bottom_surface_pattern": "monotonic", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bridge_flow": "1", - "bridge_speed": "25", - "brim_width": "5", - "brim_object_gap": "0.1", - "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "10000", - "top_surface_acceleration": "2000", - "bridge_no_support": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0.15", - "enable_arc_fitting": "1", - "outer_wall_line_width": "0.42", - "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.42", - "infill_direction": "45", - "sparse_infill_density": "15%", - "sparse_infill_pattern": "grid", - "initial_layer_acceleration": "500", - "travel_acceleration": "10000", - "inner_wall_acceleration": "5000", - "initial_layer_line_width": "0.45", - "initial_layer_print_height": "0.2", - "infill_combination": "0", - "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "15%", - "interface_shells": "0", - "ironing_flow": "15%", - "ironing_spacing": "0.1", - "ironing_speed": "15", - "ironing_type": "no ironing", - "layer_height": "0.2", - "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", - "detect_overhang_wall": "1", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "40", - "overhang_3_4_speed": "20", - "overhang_4_4_speed": "10", - "inner_wall_line_width": "0.45", - "wall_loops": "2", - "print_settings_id": "", - "raft_layers": "0", - "seam_position": "aligned", - "skirt_distance": "2", - "skirt_height": "1", - "skirt_loops": "1", - "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.42", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", - "resolution": "0.012", - "support_type": "normal(auto)", - "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.18", - "support_filament": "0", - "support_line_width": "0.42", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "2", - "support_interface_bottom_layers": "2", - "support_interface_spacing": "0.5", - "support_interface_speed": "80", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "2.5", - "support_speed": "150", - "support_threshold_angle": "30", - "support_object_xy_distance": "0.3", - "tree_support_branch_angle": "45", - "tree_support_wall_count": "0", - "detect_thin_wall": "0", - "top_surface_pattern": "monotonicline", - "top_surface_line_width": "0.42", - "top_shell_layers": "3", - "top_shell_thickness": "0.6", - "initial_layer_speed": "50", - "initial_layer_infill_speed": "80", - "outer_wall_speed": "200", - "inner_wall_speed": "300", - "internal_solid_infill_speed": "250", - "top_surface_speed": "200", - "gap_infill_speed": "200", - "sparse_infill_speed": "270", - "travel_speed": "500", - "enable_prime_tower": "0", - "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0", - "brim_type": "no_brim", - "gcode_label_objects": "0", - "exclude_object": "0", - "wall_generator": "classic", - "print_flow_ratio": "0.98", - "only_one_wall_top": "1", - "slow_down_layers": "1", - "small_perimeter_speed": "50%", - "overhang_speed_classic": "0", - "internal_bridge_speed": "50", - "outer_wall_acceleration": "5000", - "internal_solid_infill_acceleration": "7000", - "accel_to_decel_enable": "0", - "filter_out_gap_fill": "0.5", - "internal_bridge_support_thickness": "0.8", - "support_bottom_z_distance": "0.18", - "wipe_speed": "200", "compatible_printers": [ "Flashforge Adventurer 5M 0.4 Nozzle" ] -} +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M Pro 0.4 Nozzle.json b/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M Pro 0.4 Nozzle.json index cd52839a62..305ccce399 100644 --- a/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M Pro 0.4 Nozzle.json +++ b/resources/profiles/Flashforge/process/0.20mm Standard @Flashforge AD5M Pro 0.4 Nozzle.json @@ -1,125 +1,11 @@ { "type": "process", - "setting_id": "GP004", "name": "0.20mm Standard @Flashforge AD5M Pro 0.4 Nozzle", + "inherits": "fdm_process_flashforge_0.20", "from": "system", - "inherits": "fdm_process_common", + "setting_id": "GP002", "instantiation": "true", - "adaptive_layer_height": "0", - "reduce_crossing_wall": "0", - "max_travel_detour_distance": "0", - "bottom_surface_pattern": "monotonic", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bridge_flow": "1", - "bridge_speed": "25", - "brim_width": "5", - "brim_object_gap": "0.1", - "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "10000", - "top_surface_acceleration": "2000", - "bridge_no_support": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0.15", - "enable_arc_fitting": "1", - "outer_wall_line_width": "0.42", - "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.42", - "infill_direction": "45", - "sparse_infill_density": "15%", - "sparse_infill_pattern": "grid", - "initial_layer_acceleration": "500", - "travel_acceleration": "10000", - "inner_wall_acceleration": "5000", - "initial_layer_line_width": "0.45", - "initial_layer_print_height": "0.2", - "infill_combination": "0", - "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "15%", - "interface_shells": "0", - "ironing_flow": "15%", - "ironing_spacing": "0.1", - "ironing_speed": "15", - "ironing_type": "no ironing", - "layer_height": "0.2", - "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", - "detect_overhang_wall": "1", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "40", - "overhang_3_4_speed": "20", - "overhang_4_4_speed": "10", - "inner_wall_line_width": "0.45", - "wall_loops": "2", - "print_settings_id": "", - "raft_layers": "0", - "seam_position": "aligned", - "skirt_distance": "2", - "skirt_height": "1", - "skirt_loops": "1", - "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.42", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", - "resolution": "0.012", - "support_type": "normal(auto)", - "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.18", - "support_filament": "0", - "support_line_width": "0.42", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "2", - "support_interface_bottom_layers": "2", - "support_interface_spacing": "0.5", - "support_interface_speed": "80", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "2.5", - "support_speed": "150", - "support_threshold_angle": "30", - "support_object_xy_distance": "0.3", - "tree_support_branch_angle": "45", - "tree_support_wall_count": "0", - "detect_thin_wall": "0", - "top_surface_pattern": "monotonicline", - "top_surface_line_width": "0.42", - "top_shell_layers": "3", - "top_shell_thickness": "0.6", - "initial_layer_speed": "50", - "initial_layer_infill_speed": "80", - "outer_wall_speed": "200", - "inner_wall_speed": "300", - "internal_solid_infill_speed": "250", - "top_surface_speed": "200", - "gap_infill_speed": "200", - "sparse_infill_speed": "270", - "travel_speed": "500", - "enable_prime_tower": "0", - "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0", - "brim_type": "no_brim", - "gcode_label_objects": "0", - "exclude_object": "0", - "wall_generator": "classic", - "print_flow_ratio": "0.98", - "only_one_wall_top": "1", - "slow_down_layers": "1", - "small_perimeter_speed": "50%", - "overhang_speed_classic": "0", - "internal_bridge_speed": "50", - "outer_wall_acceleration": "5000", - "internal_solid_infill_acceleration": "7000", - "accel_to_decel_enable": "0", - "filter_out_gap_fill": "0.5", - "internal_bridge_support_thickness": "0.8", - "support_bottom_z_distance": "0.18", - "wipe_speed": "200", "compatible_printers": [ "Flashforge Adventurer 5M Pro 0.4 Nozzle" ] -} +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M 0.6 Nozzle.json b/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M 0.6 Nozzle.json index 9b8253fae2..76f0ab91e1 100644 --- a/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M 0.6 Nozzle.json +++ b/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M 0.6 Nozzle.json @@ -1,124 +1,11 @@ { "type": "process", - "setting_id": "GP004", "name": "0.30mm Standard @Flashforge AD5M 0.6 Nozzle", + "inherits": "fdm_process_flashforge_0.30", "from": "system", - "inherits": "fdm_process_common", + "setting_id": "GP003", "instantiation": "true", - "adaptive_layer_height": "0", - "reduce_crossing_wall": "0", - "max_travel_detour_distance": "0", - "bottom_surface_pattern": "monotonic", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bridge_flow": "1", - "bridge_speed": "25", - "brim_width": "5", - "brim_object_gap": "0.1", - "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "10000", - "top_surface_acceleration": "2000", - "bridge_no_support": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0.15", - "enable_arc_fitting": "1", - "outer_wall_line_width": "0.62", - "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.62", - "infill_direction": "45", - "sparse_infill_density": "15%", - "sparse_infill_pattern": "grid", - "initial_layer_acceleration": "500", - "travel_acceleration": "10000", - "inner_wall_acceleration": "5000", - "initial_layer_line_width": "0.62", - "initial_layer_print_height": "0.3", - "infill_combination": "0", - "sparse_infill_line_width": "0.62", - "infill_wall_overlap": "15%", - "interface_shells": "0", - "ironing_flow": "15%", - "ironing_spacing": "0.1", - "ironing_speed": "15", - "ironing_type": "no ironing", - "layer_height": "0.3", - "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", - "detect_overhang_wall": "1", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "40", - "overhang_3_4_speed": "20", - "overhang_4_4_speed": "10", - "inner_wall_line_width": "0.62", - "wall_loops": "2", - "print_settings_id": "", - "raft_layers": "0", - "seam_position": "aligned", - "skirt_distance": "2", - "skirt_height": "1", - "skirt_loops": "1", - "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.62", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", - "resolution": "0.012", - "support_type": "normal(auto)", - "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.18", - "support_filament": "0", - "support_line_width": "0.62", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "2", - "support_interface_bottom_layers": "2", - "support_interface_spacing": "0.5", - "support_interface_speed": "80", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "2.5", - "support_speed": "150", - "support_threshold_angle": "30", - "support_object_xy_distance": "0.3", - "tree_support_branch_angle": "45", - "tree_support_wall_count": "0", - "detect_thin_wall": "0", - "top_surface_pattern": "monotonicline", - "top_surface_line_width": "0.62", - "top_shell_layers": "3", - "top_shell_thickness": "0.6", - "initial_layer_speed": "45", - "initial_layer_infill_speed": "65", - "outer_wall_speed": "120", - "inner_wall_speed": "150", - "internal_solid_infill_speed": "150", - "top_surface_speed": "120", - "gap_infill_speed": "150", - "sparse_infill_speed": "100", - "travel_speed": "500", - "enable_prime_tower": "0", - "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0", - "brim_type": "no_brim", - "exclude_object": "0", - "wall_generator": "classic", - "only_one_wall_top": "1", - "small_perimeter_speed": "50%", - "overhang_speed_classic": "0", - "internal_bridge_speed": "50", - "outer_wall_acceleration": "5000", - "internal_solid_infill_acceleration": "7000", - "accel_to_decel_enable": "0", - "filter_out_gap_fill": "0.5", - "internal_bridge_support_thickness": "0.8", - "support_bottom_z_distance": "0.18", - "gcode_label_objects": "0", - "slow_down_layers": "1", - "wipe_speed": "200", "compatible_printers": [ "Flashforge Adventurer 5M 0.6 Nozzle" ] -} +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle.json b/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle.json index 230b53670a..74d314d740 100644 --- a/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle.json +++ b/resources/profiles/Flashforge/process/0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle.json @@ -1,233 +1,11 @@ { "type": "process", - "setting_id": "GP004", "name": "0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle", + "inherits": "fdm_process_flashforge_0.30", "from": "system", - "inherits": "fdm_process_common", + "setting_id": "GP004", "instantiation": "true", - "accel_to_decel_enable": "0", - "accel_to_decel_factor": "50%", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bottom_solid_infill_flow_ratio": "1", - "bottom_surface_pattern": "monotonic", - "bridge_acceleration": "50%", - "bridge_angle": "0", - "bridge_density": "100%", - "bridge_flow": "1", - "bridge_no_support": "0", - "bridge_speed": "25", - "brim_ears_detection_length": "1", - "brim_ears_max_angle": "125", - "brim_object_gap": "0.1", - "brim_type": "no_brim", - "brim_width": "5", "compatible_printers": [ "Flashforge Adventurer 5M Pro 0.6 Nozzle" - ], - "compatible_printers_condition": "", - "default_acceleration": "10000", - "default_jerk": "0", - "detect_narrow_internal_solid_infill": "1", - "detect_overhang_wall": "1", - "detect_thin_wall": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0.15", - "enable_arc_fitting": "1", - "enable_overhang_speed": "1", - "enable_prime_tower": "0", - "enable_support": "0", - "enforce_support_layers": "0", - "ensure_vertical_shell_thickness": "1", - "exclude_object": "0", - "extra_perimeters_on_overhangs": "0", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", - "filter_out_gap_fill": "0.5", - "flush_into_infill": "0", - "flush_into_objects": "0", - "flush_into_support": "1", - "fuzzy_skin": "none", - "fuzzy_skin_point_distance": "0.8", - "fuzzy_skin_thickness": "0.3", - "gap_infill_speed": "150", - "gcode_add_line_number": "0", - "gcode_comments": "0", - "gcode_label_objects": "0", - "independent_support_layer_height": "1", - "infill_anchor": "400%", - "infill_anchor_max": "20", - "infill_combination": "0", - "infill_direction": "45", - "infill_jerk": "9", - "infill_wall_overlap": "15%", - "initial_layer_acceleration": "500", - "initial_layer_infill_speed": "65", - "initial_layer_jerk": "9", - "initial_layer_line_width": "0.62", - "initial_layer_min_bead_width": "85%", - "initial_layer_print_height": "0.3", - "initial_layer_speed": "45", - "initial_layer_travel_speed": "100%", - "inner_wall_acceleration": "5000", - "inner_wall_jerk": "9", - "inner_wall_line_width": "0.62", - "inner_wall_speed": "150", - "interface_shells": "0", - "internal_bridge_speed": "50", - "internal_bridge_support_thickness": "0.8", - "internal_solid_infill_acceleration": "7000", - "internal_solid_infill_line_width": "0.62", - "internal_solid_infill_pattern": "monotonic", - "internal_solid_infill_speed": "150", - "ironing_flow": "10%", - "ironing_pattern": "zig-zag", - "ironing_spacing": "0.15", - "ironing_speed": "30", - "ironing_type": "no ironing", - "layer_height": "0.3", - "line_width": "0.62", - "make_overhang_printable": "0", - "make_overhang_printable_angle": "55", - "make_overhang_printable_hole_size": "0", - "max_bridge_length": "10", - "max_travel_detour_distance": "0", - "min_bead_width": "85%", - "min_feature_size": "25%", - "min_width_top_surface": "300%", - "minimum_sparse_infill_area": "15", - "notes": "", - "only_one_wall_first_layer": "0", - "only_one_wall_top": "1", - "ooze_prevention": "0", - "outer_wall_acceleration": "5000", - "outer_wall_jerk": "9", - "outer_wall_line_width": "0.62", - "outer_wall_speed": "120", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "40", - "overhang_3_4_speed": "20", - "overhang_4_4_speed": "10", - "overhang_speed_classic": "0", - "post_process": [], - "precise_outer_wall": "0", - "prime_tower_brim_width": "3", - "prime_tower_width": "35", - "prime_volume": "45", - "print_flow_ratio": "1", - "print_sequence": "by layer", - "print_settings_id": "0.30mm Standard @Flashforge AD5M Pro 0.6 Nozzle", - "raft_contact_distance": "0.1", - "raft_expansion": "1.5", - "raft_first_layer_density": "90%", - "raft_first_layer_expansion": "2", - "raft_layers": "0", - "reduce_crossing_wall": "0", - "reduce_infill_retraction": "1", - "resolution": "0.012", - "role_based_wipe_speed": "1", - "seam_gap": "10%", - "seam_position": "aligned", - "single_extruder_multi_material_priming": "1", - "skirt_distance": "2", - "skirt_height": "1", - "skirt_loops": "1", - "slice_closing_radius": "0.049", - "slicing_mode": "regular", - "slow_down_layers": "1", - "small_perimeter_speed": "50%", - "small_perimeter_threshold": "0", - "solid_infill_filament": "1", - "sparse_infill_acceleration": "100%", - "sparse_infill_density": "15%", - "sparse_infill_filament": "1", - "sparse_infill_line_width": "0.62", - "sparse_infill_pattern": "grid", - "sparse_infill_speed": "100", - "spiral_mode": "0", - "staggered_inner_seams": "0", - "standby_temperature_delta": "-5", - "support_angle": "0", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "2.5", - "support_bottom_interface_spacing": "0.5", - "support_bottom_z_distance": "0.18", - "support_critical_regions_only": "0", - "support_expansion": "0", - "support_filament": "0", - "support_interface_bottom_layers": "2", - "support_interface_filament": "0", - "support_interface_loop_pattern": "0", - "support_interface_pattern": "rectilinear", - "support_interface_spacing": "0.5", - "support_interface_speed": "80", - "support_interface_top_layers": "2", - "support_line_width": "0.62", - "support_object_xy_distance": "0.3", - "support_on_build_plate_only": "0", - "support_remove_small_overhang": "1", - "support_speed": "150", - "support_style": "default", - "support_threshold_angle": "30", - "support_top_z_distance": "0.18", - "support_type": "normal(auto)", - "thick_bridges": "0", - "timelapse_type": "0", - "top_shell_layers": "3", - "top_shell_thickness": "0.6", - "top_solid_infill_flow_ratio": "1", - "top_surface_acceleration": "2000", - "top_surface_jerk": "9", - "top_surface_line_width": "0.62", - "top_surface_pattern": "monotonicline", - "top_surface_speed": "120", - "travel_acceleration": "10000", - "travel_jerk": "12", - "travel_speed": "500", - "travel_speed_z": "0", - "tree_support_adaptive_layer_height": "1", - "tree_support_angle_slow": "25", - "tree_support_auto_brim": "1", - "tree_support_branch_angle": "45", - "tree_support_branch_angle_organic": "40", - "tree_support_branch_diameter": "5", - "tree_support_branch_diameter_angle": "5", - "tree_support_branch_diameter_double_wall": "3", - "tree_support_branch_diameter_organic": "2", - "tree_support_branch_distance": "5", - "tree_support_branch_distance_organic": "1", - "tree_support_brim_width": "3", - "tree_support_tip_diameter": "0.8", - "tree_support_top_rate": "30%", - "tree_support_wall_count": "0", - "version": "1.5.1.2", - "wall_distribution_count": "1", - "wall_filament": "1", - "wall_generator": "classic", - "wall_infill_order": "inner wall/outer wall/infill", - "wall_loops": "2", - "wall_transition_angle": "10", - "wall_transition_filter_deviation": "25%", - "wall_transition_length": "100%", - "wipe_on_loops": "0", - "wipe_speed": "200", - "wipe_tower_bridging": "10", - "wipe_tower_cone_angle": "0", - "wipe_tower_extra_spacing": "100%", - "wipe_tower_extruder": "0", - "wipe_tower_no_sparse_layers": "0", - "wipe_tower_rotation_angle": "0", - "wiping_volumes_extruders": [ - "70", - "70", - "70", - "70", - "70", - "70", - "70", - "70", - "70", - "70" - ], - "xy_contour_compensation": "0", - "xy_hole_compensation": "0" -} + ] +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/fdm_process_common.json b/resources/profiles/Flashforge/process/fdm_process_common.json index 7398bcd481..921718b995 100644 --- a/resources/profiles/Flashforge/process/fdm_process_common.json +++ b/resources/profiles/Flashforge/process/fdm_process_common.json @@ -8,12 +8,11 @@ "bridge_flow": "0.95", "bridge_speed": "25", "brim_width": "5", - "compatible_printers": [], "print_sequence": "by layer", "default_acceleration": "10000", "bridge_no_support": "0", "elefant_foot_compensation": "0.1", - "outer_wall_line_width": "0.4", + "outer_wall_line_width": "0.42", "outer_wall_speed": "120", "line_width": "0.45", "infill_direction": "45", @@ -25,13 +24,13 @@ "gap_infill_speed": "30", "infill_combination": "0", "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "25%", + "infill_wall_overlap": "15%", "sparse_infill_speed": "50", "interface_shells": "0", "detect_overhang_wall": "0", "reduce_infill_retraction": "0", "filename_format": "{input_filename_base}.gcode", - "wall_loops": "3", + "wall_loops": "2", "inner_wall_line_width": "0.45", "inner_wall_speed": "40", "print_settings_id": "", @@ -54,17 +53,19 @@ "support_interface_top_layers": "2", "support_interface_spacing": "0", "support_interface_speed": "80", - "support_base_pattern": "rectilinear", + "support_interface_pattern": "auto", + "support_base_pattern": "default", "support_base_pattern_spacing": "2", "support_speed": "40", "support_threshold_angle": "40", "support_object_xy_distance": "0.5", "detect_thin_wall": "0", - "top_surface_line_width": "0.4", + "top_surface_line_width": "0.42", "top_surface_speed": "30", "travel_speed": "400", "enable_prime_tower": "0", "prime_tower_width": "60", "xy_hole_compensation": "0", - "xy_contour_compensation": "0" -} + "xy_contour_compensation": "0", + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/fdm_process_flashforge_0.20.json b/resources/profiles/Flashforge/process/fdm_process_flashforge_0.20.json new file mode 100644 index 0000000000..506873d376 --- /dev/null +++ b/resources/profiles/Flashforge/process/fdm_process_flashforge_0.20.json @@ -0,0 +1,22 @@ +{ + "type": "process", + "name": "fdm_process_flashforge_0.20", + "inherits": "fdm_process_flashforge_common", + "from": "system", + "instantiation": "false", + "initial_layer_infill_speed": "80", + "outer_wall_speed": "200", + "inner_wall_speed": "300", + "internal_solid_infill_speed": "250", + "gap_infill_speed": "200", + "sparse_infill_speed": "270", + "small_perimeter_speed": "50%", + "overhang_speed_classic": "0", + "internal_bridge_speed": "50", + "internal_solid_infill_acceleration": "7000", + "accel_to_decel_enable": "0", + "filter_out_gap_fill": "0.5", + "gcode_label_objects": "0", + "slow_down_layers": "1", + "wipe_speed": "200" +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/fdm_process_flashforge_0.30.json b/resources/profiles/Flashforge/process/fdm_process_flashforge_0.30.json new file mode 100644 index 0000000000..6a3cfe566f --- /dev/null +++ b/resources/profiles/Flashforge/process/fdm_process_flashforge_0.30.json @@ -0,0 +1,30 @@ +{ + "type": "process", + "name": "fdm_process_flashforge_0.30", + "inherits": "fdm_process_flashforge_common", + "from": "system", + "instantiation": "false", + "layer_height": "0.3", + "initial_layer_print_height": "0.3", + "line_width": "0.62", + "outer_wall_line_width": "0.62", + "initial_layer_line_width": "0.62", + "sparse_infill_line_width": "0.62", + "inner_wall_line_width": "0.62", + "internal_solid_infill_line_width": "0.62", + "support_line_width": "0.62", + "top_surface_line_width": "0.62", + "initial_layer_speed": "45", + "inner_wall_speed": "150", + "top_surface_speed": "120", + "gap_infill_speed": "150", + "small_perimeter_speed": "50%", + "overhang_speed_classic": "0", + "internal_bridge_speed": "50", + "internal_solid_infill_acceleration": "7000", + "accel_to_decel_enable": "0", + "filter_out_gap_fill": "0.5", + "gcode_label_objects": "0", + "slow_down_layers": "1", + "wipe_speed": "200" +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/fdm_process_flashforge_common.json b/resources/profiles/Flashforge/process/fdm_process_flashforge_common.json index f324f88404..272172901f 100644 --- a/resources/profiles/Flashforge/process/fdm_process_flashforge_common.json +++ b/resources/profiles/Flashforge/process/fdm_process_flashforge_common.json @@ -1,104 +1,72 @@ { "type": "process", "name": "fdm_process_flashforge_common", + "inherits": "fdm_process_common", "from": "system", "instantiation": "false", - "inherits": "fdm_process_common", - "adaptive_layer_height": "0", - "reduce_crossing_wall": "0", "max_travel_detour_distance": "0", "bottom_surface_pattern": "monotonic", "bottom_shell_layers": "3", "bottom_shell_thickness": "0", - "bridge_flow": "0.95", - "bridge_speed": "25", - "brim_width": "5", + "bridge_flow": "1", "brim_object_gap": "0.1", "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "500", - "top_surface_acceleration": "500", - "bridge_no_support": "0", "draft_shield": "disabled", - "elefant_foot_compensation": "0", - "enable_arc_fitting": "0", - "outer_wall_line_width": "0.4", + "elefant_foot_compensation": "0.15", + "enable_arc_fitting": "1", + "outer_wall_acceleration": "5000", "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.4", - "infill_direction": "45", - "sparse_infill_density": "15%", - "sparse_infill_pattern": "grid", + "line_width": "0.42", + "internal_bridge_support_thickness": "0.8", "initial_layer_acceleration": "500", - "travel_acceleration": "700", - "inner_wall_acceleration": "500", - "initial_layer_line_width": "0.5", - "initial_layer_print_height": "0.2", - "infill_combination": "0", - "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "23%", - "interface_shells": "0", + "travel_acceleration": "10000", + "inner_wall_acceleration": "5000", + "initial_layer_line_width": "0.45", + "sparse_infill_speed": "100", "ironing_flow": "15%", "ironing_spacing": "0.1", "ironing_speed": "15", "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", - "overhang_2_4_speed": "20", - "overhang_3_4_speed": "15", + "overhang_2_4_speed": "40", + "overhang_3_4_speed": "20", "overhang_4_4_speed": "10", - "inner_wall_line_width": "0.45", - "wall_loops": "3", - "print_settings_id": "", - "raft_layers": "0", + "only_one_wall_top": "1", "seam_position": "aligned", - "skirt_distance": "2", "skirt_height": "1", - "skirt_loops": "1", + "skirt_loops": "2", + "brim_type": "no_brim", + "exclude_object": "0", + "wall_generator": "classic", "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.4", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", + "internal_solid_infill_line_width": "0.42", "resolution": "0.012", "support_type": "normal(auto)", "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.2", - "support_filament": "0", - "support_line_width": "0.4", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "2", + "support_top_z_distance": "0.18", + "support_bottom_z_distance": "0.18", "support_interface_bottom_layers": "2", "support_interface_spacing": "0.5", - "support_interface_speed": "80", "support_base_pattern": "rectilinear", "support_base_pattern_spacing": "2.5", "support_speed": "150", "support_threshold_angle": "30", - "support_object_xy_distance": "0.35", + "support_object_xy_distance": "0.3", "tree_support_branch_angle": "45", "tree_support_wall_count": "0", - "detect_thin_wall": "0", - "top_surface_pattern": "monotonic", - "top_surface_line_width": "0.4", + "top_surface_pattern": "monotonicline", + "top_surface_acceleration": "2000", "top_shell_layers": "3", - "top_shell_thickness": "0.8", - "initial_layer_speed": "15", - "initial_layer_infill_speed": "20", - "outer_wall_speed": "25", - "inner_wall_speed": "40", - "internal_solid_infill_speed": "40", - "top_surface_speed": "30", - "gap_infill_speed": "30", - "sparse_infill_speed": "50", - "travel_speed": "150", - "enable_prime_tower": "0", + "top_shell_thickness": "0.6", + "initial_layer_speed": "50", + "initial_layer_infill_speed": "65", + "internal_solid_infill_speed": "150", + "top_surface_speed": "200", + "travel_speed": "500", "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0" -} + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/Flashforge/process/fdm_process_klipper_common.json b/resources/profiles/Flashforge/process/fdm_process_klipper_common.json deleted file mode 100644 index cfd544f1b6..0000000000 --- a/resources/profiles/Flashforge/process/fdm_process_klipper_common.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "type": "process", - "name": "fdm_process_klipper_common", - "from": "system", - "instantiation": "false", - "inherits": "fdm_process_common", - "adaptive_layer_height": "0", - "reduce_crossing_wall": "0", - "max_travel_detour_distance": "0", - "bottom_surface_pattern": "monotonic", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bridge_flow": "0.95", - "bridge_speed": "25", - "brim_width": "5", - "brim_object_gap": "0.1", - "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "5000", - "top_surface_acceleration": "3000", - "travel_acceleration": "7000", - "inner_wall_acceleration": "5000", - "outer_wall_acceleration": "3000", - "bridge_no_support": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0", - "outer_wall_line_width": "0.4", - "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.4", - "infill_direction": "45", - "sparse_infill_density": "15%", - "sparse_infill_pattern": "grid", - "initial_layer_acceleration": "500", - "initial_layer_line_width": "0.5", - "initial_layer_print_height": "0.2", - "infill_combination": "0", - "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "25%", - "interface_shells": "0", - "ironing_flow": "10%", - "ironing_spacing": "0.15", - "ironing_speed": "30", - "ironing_type": "no ironing", - "layer_height": "0.2", - "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", - "detect_overhang_wall": "1", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "50", - "overhang_3_4_speed": "30", - "overhang_4_4_speed": "10", - "inner_wall_line_width": "0.45", - "wall_loops": "3", - "print_settings_id": "", - "raft_layers": "0", - "seam_position": "aligned", - "skirt_distance": "2", - "skirt_height": "1", - "skirt_loops": "1", - "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.4", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", - "resolution": "0.012", - "support_type": "normal(auto)", - "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.2", - "support_filament": "0", - "support_line_width": "0.4", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "2", - "support_interface_bottom_layers": "2", - "support_interface_spacing": "0.5", - "support_interface_speed": "80", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "2.5", - "support_speed": "150", - "support_threshold_angle": "30", - "support_object_xy_distance": "0.35", - "tree_support_branch_angle": "45", - "tree_support_wall_count": "0", - "detect_thin_wall": "0", - "top_surface_pattern": "monotonic", - "top_surface_line_width": "0.4", - "top_shell_layers": "3", - "top_shell_thickness": "0.8", - "initial_layer_speed": "50", - "initial_layer_infill_speed": "105", - "outer_wall_speed": "120", - "inner_wall_speed": "200", - "internal_solid_infill_speed": "200", - "top_surface_speed": "100", - "gap_infill_speed": "100", - "sparse_infill_speed": "200", - "travel_speed": "350", - "enable_prime_tower": "0", - "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0", - "enable_arc_fitting": "0", - "compatible_printers": [ - "MyKlipper 0.4 nozzle" - ] -} diff --git a/resources/profiles/FlyingBear.json b/resources/profiles/FlyingBear.json new file mode 100644 index 0000000000..459c90879c --- /dev/null +++ b/resources/profiles/FlyingBear.json @@ -0,0 +1,102 @@ +{ + "name": "FlyingBear", + "version": "01.08.00.00", + "force_update": "1", + "description": "FlyingBear configurations", + "machine_model_list": [ + { + "name": "FlyingBear Reborn3", + "sub_path": "machine/FlyingBear Reborn3.json" + } + ], + "process_list": [ + { + "name": "fdm_process_common", + "sub_path": "process/fdm_process_common.json" + }, + { + "name": "0.08mm Extra Fine @FlyingBear Reborn3", + "sub_path": "process/0.08mm Extra Fine @FlyingBear Reborn3.json" + }, + { + "name": "0.12mm Fine @FlyingBear Reborn3", + "sub_path": "process/0.12mm Fine @FlyingBear Reborn3.json" + }, + { + "name": "0.20mm Standard @FlyingBear Reborn3", + "sub_path": "process/0.20mm Standard @FlyingBear Reborn3.json" + }, + { + "name": "0.24mm Draft @FlyingBear Reborn3", + "sub_path": "process/0.24mm Draft @FlyingBear Reborn3.json" + } + ], + "filament_list": [ + { + "name": "fdm_filament_common", + "sub_path": "filament/fdm_filament_common.json" + }, + { + "name": "fdm_filament_pla", + "sub_path": "filament/fdm_filament_pla.json" + }, + { + "name": "fdm_filament_tpu", + "sub_path": "filament/fdm_filament_tpu.json" + }, + { + "name": "fdm_filament_pet", + "sub_path": "filament/fdm_filament_pet.json" + }, + { + "name": "fdm_filament_abs", + "sub_path": "filament/fdm_filament_abs.json" + }, + { + "name": "fdm_filament_pc", + "sub_path": "filament/fdm_filament_pc.json" + }, + { + "name": "fdm_filament_pa", + "sub_path": "filament/fdm_filament_pa.json" + }, + { + "name": "FlyingBear Generic PLA", + "sub_path": "filament/FlyingBear Generic PLA.json" + }, + { + "name": "FlyingBear Generic PETG", + "sub_path": "filament/FlyingBear Generic PETG.json" + }, + { + "name": "FlyingBear Generic ABS", + "sub_path": "filament/FlyingBear Generic ABS.json" + }, + { + "name": "FlyingBear Generic TPU", + "sub_path": "filament/FlyingBear Generic TPU.json" + }, + { + "name": "FlyingBear Generic PC", + "sub_path": "filament/FlyingBear Generic PC.json" + }, + { + "name": "FlyingBear Generic PA-CF", + "sub_path": "filament/FlyingBear Generic PA-CF.json" + } + ], + "machine_list": [ + { + "name": "fdm_machine_common", + "sub_path": "machine/fdm_machine_common.json" + }, + { + "name": "fdm_klipper_common", + "sub_path": "machine/fdm_klipper_common.json" + }, + { + "name": "FlyingBear Reborn3 0.4 nozzle", + "sub_path": "machine/FlyingBear Reborn3 0.4 nozzle.json" + } + ] +} diff --git a/resources/profiles/FlyingBear/FlyingBear Reborn3-bed.stl b/resources/profiles/FlyingBear/FlyingBear Reborn3-bed.stl new file mode 100644 index 0000000000..58a5529802 Binary files /dev/null and b/resources/profiles/FlyingBear/FlyingBear Reborn3-bed.stl differ diff --git a/resources/profiles/FlyingBear/FlyingBear Reborn3_cover.png b/resources/profiles/FlyingBear/FlyingBear Reborn3_cover.png new file mode 100644 index 0000000000..7d79202ba4 Binary files /dev/null and b/resources/profiles/FlyingBear/FlyingBear Reborn3_cover.png differ diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic ABS.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic ABS.json new file mode 100644 index 0000000000..4cc0e64e39 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic ABS.json @@ -0,0 +1,18 @@ +{ + "type": "filament", + "filament_id": "GFB99", + "setting_id": "GFSA04", + "name": "FlyingBear Generic ABS", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_abs", + "filament_flow_ratio": [ + "0.931" + ], + "filament_max_volumetric_speed": [ + "16" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic PA-CF.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic PA-CF.json new file mode 100644 index 0000000000..7a59fc5875 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic PA-CF.json @@ -0,0 +1,25 @@ +{ + "type": "filament", + "filament_id": "GFN98", + "setting_id": "GFSA04", + "name": "FlyingBear Generic PA-CF", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pa", + "filament_type": [ + "PA-CF" + ], + "nozzle_temperature_initial_layer": [ + "280" + ], + "nozzle_temperature": [ + "280" + ], + "filament_max_volumetric_speed": [ + "8" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + + ] + } diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic PC.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic PC.json new file mode 100644 index 0000000000..d8486c08e3 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic PC.json @@ -0,0 +1,18 @@ +{ + "type": "filament", + "filament_id": "GFC99", + "setting_id": "GFSA04", + "name": "FlyingBear Generic PC", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pc", + "filament_max_volumetric_speed": [ + "12" + ], + "filament_flow_ratio": [ + "0.931" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] + } diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic PETG.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic PETG.json new file mode 100644 index 0000000000..1257a0c6cd --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic PETG.json @@ -0,0 +1,48 @@ +{ + "type": "filament", + "filament_id": "GFG99", + "setting_id": "GFSA04", + "name": "FlyingBear Generic PETG", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pet", + "reduce_fan_stop_start_freq": [ + "1" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "fan_cooling_layer_time": [ + "30" + ], + "overhang_fan_speed": [ + "90" + ], + "overhang_fan_threshold": [ + "25%" + ], + "fan_max_speed": [ + "90" + ], + "fan_min_speed": [ + "40" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "8" + ], + "filament_flow_ratio": [ + "0.931" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic PLA.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic PLA.json new file mode 100644 index 0000000000..ef7dcfc96a --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic PLA.json @@ -0,0 +1,21 @@ +{ + "type": "filament", + "filament_id": "GFL99", + "setting_id": "GFSA04", + "name": "FlyingBear Generic PLA", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pla", + "filament_flow_ratio": [ + "0.931" + ], + "filament_max_volumetric_speed": [ + "26" + ], + "slow_down_layer_time": [ + "8" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/filament/FlyingBear Generic TPU.json b/resources/profiles/FlyingBear/filament/FlyingBear Generic TPU.json new file mode 100644 index 0000000000..a2fe4bff86 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/FlyingBear Generic TPU.json @@ -0,0 +1,15 @@ +{ + "type": "filament", + "filament_id": "GFU99", + "setting_id": "GFSA04", + "name": "FlyingBear Generic TPU", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_tpu", + "filament_max_volumetric_speed": [ + "3.2" + ], + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_abs.json b/resources/profiles/FlyingBear/filament/fdm_filament_abs.json new file mode 100644 index 0000000000..97cbf02b1b --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_abs.json @@ -0,0 +1,88 @@ +{ + "type": "filament", + "name": "fdm_filament_abs", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "105" + ], + "eng_plate_temp" : [ + "105" + ], + "hot_plate_temp" : [ + "105" + ], + "textured_plate_temp" : [ + "100" + ], + "cool_plate_temp_initial_layer" : [ + "105" + ], + "eng_plate_temp_initial_layer" : [ + "105" + ], + "hot_plate_temp_initial_layer" : [ + "100" + ], + "textured_plate_temp_initial_layer" : [ + "105" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "30" + ], + "filament_max_volumetric_speed": [ + "16" + ], + "filament_type": [ + "ABS" + ], + "filament_density": [ + "1.04" + ], + "filament_cost": [ + "20" + ], + "nozzle_temperature_initial_layer": [ + "250" + ], + "reduce_fan_stop_start_freq": [ + "0" + ], + "fan_max_speed": [ + "20" + ], + "fan_min_speed": [ + "10" + ], + "overhang_fan_threshold": [ + "25%" + ], + "overhang_fan_speed": [ + "80" + ], + "nozzle_temperature": [ + "250" + ], + "temperature_vitrification": [ + "110" + ], + "nozzle_temperature_range_low": [ + "240" + ], + "nozzle_temperature_range_high": [ + "270" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "3" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_common.json b/resources/profiles/FlyingBear/filament/fdm_filament_common.json new file mode 100644 index 0000000000..e8244c65c4 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_common.json @@ -0,0 +1,144 @@ +{ + "type": "filament", + "name": "fdm_filament_common", + "from": "system", + "instantiation": "false", + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "60" + ], + "hot_plate_temp" : [ + "60" + ], + "textured_plate_temp" : [ + "60" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "60" + ], + "hot_plate_temp_initial_layer" : [ + "60" + ], + "textured_plate_temp_initial_layer" : [ + "60" + ], + "overhang_fan_threshold": [ + "95%" + ], + "overhang_fan_speed": [ + "100" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_flow_ratio": [ + "1" + ], + "reduce_fan_stop_start_freq": [ + "0" + ], + "fan_cooling_layer_time": [ + "60" + ], + "filament_cost": [ + "0" + ], + "filament_density": [ + "0" + ], + "filament_deretraction_speed": [ + "nil" + ], + "filament_diameter": [ + "1.75" + ], + "filament_max_volumetric_speed": [ + "0" + ], + "filament_minimal_purge_on_wipe_tower": [ + "15" + ], + "filament_retraction_minimum_travel": [ + "nil" + ], + "filament_retract_before_wipe": [ + "nil" + ], + "filament_retract_when_changing_layer": [ + "nil" + ], + "filament_retraction_length": [ + "nil" + ], + "filament_z_hop": [ + "nil" + ], + "filament_z_hop_types": [ + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retraction_speed": [ + "nil" + ], + "filament_settings_id": [ + "" + ], + "filament_soluble": [ + "0" + ], + "filament_type": [ + "PLA" + ], + "filament_vendor": [ + "Generic" + ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], + "bed_type": [ + "Cool Plate" + ], + "nozzle_temperature_initial_layer": [ + "200" + ], + "full_fan_speed_layer": [ + "0" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "35" + ], + "slow_down_min_speed": [ + "10" + ], + "slow_down_layer_time": [ + "8" + ], + "filament_start_gcode": [ + "; Filament gcode\n" + ], + "nozzle_temperature": [ + "200" + ], + "temperature_vitrification": [ + "100" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_pa.json b/resources/profiles/FlyingBear/filament/fdm_filament_pa.json new file mode 100644 index 0000000000..1111001d65 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_pa.json @@ -0,0 +1,85 @@ +{ + "type": "filament", + "name": "fdm_filament_pa", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "0" + ], + "eng_plate_temp" : [ + "100" + ], + "hot_plate_temp" : [ + "100" + ], + "textured_plate_temp" : [ + "100" + ], + "cool_plate_temp_initial_layer" : [ + "0" + ], + "eng_plate_temp_initial_layer" : [ + "100" + ], + "hot_plate_temp_initial_layer" : [ + "100" + ], + "textured_plate_temp_initial_layer" : [ + "100" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "5" + ], + "filament_max_volumetric_speed": [ + "8" + ], + "filament_type": [ + "PA" + ], + "filament_density": [ + "1.04" + ], + "filament_cost": [ + "20" + ], + "nozzle_temperature_initial_layer": [ + "280" + ], + "reduce_fan_stop_start_freq": [ + "0" + ], + "fan_max_speed": [ + "30" + ], + "fan_min_speed": [ + "10" + ], + "overhang_fan_speed": [ + "40" + ], + "nozzle_temperature": [ + "280" + ], + "temperature_vitrification": [ + "108" + ], + "nozzle_temperature_range_low": [ + "270" + ], + "nozzle_temperature_range_high": [ + "300" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "2" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_pc.json b/resources/profiles/FlyingBear/filament/fdm_filament_pc.json new file mode 100644 index 0000000000..d75eeaf55d --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_pc.json @@ -0,0 +1,88 @@ +{ + "type": "filament", + "name": "fdm_filament_pc", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "0" + ], + "eng_plate_temp" : [ + "110" + ], + "hot_plate_temp" : [ + "110" + ], + "textured_plate_temp" : [ + "110" + ], + "cool_plate_temp_initial_layer" : [ + "0" + ], + "eng_plate_temp_initial_layer" : [ + "110" + ], + "hot_plate_temp_initial_layer" : [ + "110" + ], + "textured_plate_temp_initial_layer" : [ + "110" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "30" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "filament_type": [ + "PC" + ], + "filament_density": [ + "1.04" + ], + "filament_cost": [ + "20" + ], + "nozzle_temperature_initial_layer": [ + "240" + ], + "reduce_fan_stop_start_freq": [ + "0" + ], + "fan_max_speed": [ + "20" + ], + "fan_min_speed": [ + "10" + ], + "overhang_fan_threshold": [ + "25%" + ], + "overhang_fan_speed": [ + "60" + ], + "nozzle_temperature": [ + "240" + ], + "temperature_vitrification": [ + "140" + ], + "nozzle_temperature_range_low": [ + "240" + ], + "nozzle_temperature_range_high": [ + "280" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "2" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_pet.json b/resources/profiles/FlyingBear/filament/fdm_filament_pet.json new file mode 100644 index 0000000000..9b5735fa90 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_pet.json @@ -0,0 +1,82 @@ +{ + "type": "filament", + "name": "fdm_filament_pet", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "0" + ], + "hot_plate_temp" : [ + "70" + ], + "textured_plate_temp" : [ + "80" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "0" + ], + "hot_plate_temp_initial_layer" : [ + "70" + ], + "textured_plate_temp_initial_layer" : [ + "80" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "30" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "filament_type": [ + "PETG" + ], + "filament_density": [ + "1.27" + ], + "filament_cost": [ + "30" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "fan_max_speed": [ + "90" + ], + "fan_min_speed": [ + "40" + ], + "overhang_fan_speed": [ + "90" + ], + "nozzle_temperature": [ + "220" + ], + "temperature_vitrification": [ + "80" + ], + "nozzle_temperature_range_low": [ + "220" + ], + "nozzle_temperature_range_high": [ + "260" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_pla.json b/resources/profiles/FlyingBear/filament/fdm_filament_pla.json new file mode 100644 index 0000000000..8ef6848423 --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_pla.json @@ -0,0 +1,94 @@ +{ + "type": "filament", + "name": "fdm_filament_pla", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "fan_cooling_layer_time": [ + "80" + ], + "filament_max_volumetric_speed": [ + "26" + ], + "filament_type": [ + "PLA" + ], + "filament_density": [ + "1.24" + ], + "filament_cost": [ + "20" + ], + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "60" + ], + "hot_plate_temp" : [ + "60" + ], + "textured_plate_temp" : [ + "60" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "60" + ], + "hot_plate_temp_initial_layer" : [ + "60" + ], + "textured_plate_temp_initial_layer" : [ + "60" + ], + "nozzle_temperature_initial_layer": [ + "200" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "50" + ], + "overhang_fan_speed": [ + "100" + ], + "overhang_fan_threshold": [ + "50%" + ], + "close_fan_the_first_x_layers": [ + "1" + ], + "nozzle_temperature": [ + "200" + ], + "temperature_vitrification": [ + "60" + ], + "nozzle_temperature_range_low": [ + "190" + ], + "nozzle_temperature_range_high": [ + "230" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "8" + ], + "additional_cooling_fan_speed": [ + "70" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/FlyingBear/filament/fdm_filament_tpu.json b/resources/profiles/FlyingBear/filament/fdm_filament_tpu.json new file mode 100644 index 0000000000..012be60aba --- /dev/null +++ b/resources/profiles/FlyingBear/filament/fdm_filament_tpu.json @@ -0,0 +1,88 @@ +{ + "type": "filament", + "name": "fdm_filament_tpu", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "30" + ], + "eng_plate_temp" : [ + "30" + ], + "hot_plate_temp" : [ + "40" + ], + "textured_plate_temp" : [ + "35" + ], + "cool_plate_temp_initial_layer" : [ + "30" + ], + "eng_plate_temp_initial_layer" : [ + "30" + ], + "hot_plate_temp_initial_layer" : [ + "40" + ], + "textured_plate_temp_initial_layer" : [ + "35" + ], + "fan_cooling_layer_time": [ + "100" + ], + "filament_max_volumetric_speed": [ + "3.2" + ], + "filament_type": [ + "TPU" + ], + "filament_density": [ + "1.24" + ], + "filament_cost": [ + "20" + ], + "filament_retraction_length": [ + "0.4" + ], + "nozzle_temperature_initial_layer": [ + "210" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], + "overhang_fan_speed": [ + "100" + ], + "additional_cooling_fan_speed": [ + "70" + ], + "close_fan_the_first_x_layers": [ + "1" + ], + "nozzle_temperature": [ + "210" + ], + "temperature_vitrification": [ + "60" + ], + "nozzle_temperature_range_low": [ + "200" + ], + "nozzle_temperature_range_high": [ + "250" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/FlyingBear/lyingBear Reborn3-texture.png b/resources/profiles/FlyingBear/lyingBear Reborn3-texture.png new file mode 100644 index 0000000000..928fc8e0d5 Binary files /dev/null and b/resources/profiles/FlyingBear/lyingBear Reborn3-texture.png differ diff --git a/resources/profiles/FlyingBear/machine/FlyingBear Reborn3 0.4 nozzle.json b/resources/profiles/FlyingBear/machine/FlyingBear Reborn3 0.4 nozzle.json new file mode 100644 index 0000000000..2a8d0a2e6f --- /dev/null +++ b/resources/profiles/FlyingBear/machine/FlyingBear Reborn3 0.4 nozzle.json @@ -0,0 +1,22 @@ +{ + "type": "machine", + "setting_id": "GM001", + "name": "FlyingBear Reborn3 0.4 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_klipper_common", + "printer_model": "FlyingBear Reborn3", + "nozzle_diameter": [ + "0.4" + ], + "z_hop": [ + "0" + ], + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "350" +} diff --git a/resources/profiles/FlyingBear/machine/FlyingBear Reborn3.json b/resources/profiles/FlyingBear/machine/FlyingBear Reborn3.json new file mode 100644 index 0000000000..cd4ae4dcf6 --- /dev/null +++ b/resources/profiles/FlyingBear/machine/FlyingBear Reborn3.json @@ -0,0 +1,12 @@ +{ + "type": "machine_model", + "name": "FlyingBear Reborn3", + "model_id": "FlyingBear Reborn3", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "FlyingBearDesign", + "bed_model": "FlyingBear Reborn3-bed.stl", + "bed_texture": "FlyingBear Reborn3-texture.png", + "hotend_model": "", + "default_materials": "FlyingBear Generic ABS;FlyingBear Generic PA-CF;FlyingBear Generic PC;FlyingBear Generic PETG;FlyingBear Generic PLA;FlyingBear Generic TPU" +} \ No newline at end of file diff --git a/resources/profiles/FlyingBear/machine/fdm_klipper_common.json b/resources/profiles/FlyingBear/machine/fdm_klipper_common.json new file mode 100644 index 0000000000..870c4ba2b1 --- /dev/null +++ b/resources/profiles/FlyingBear/machine/fdm_klipper_common.json @@ -0,0 +1,210 @@ +{ + "type": "machine", + "name": "fdm_klipper_common", + "from": "system", + "instantiation": "false", + "inherits": "fdm_machine_common", + "gcode_flavor": "klipper", + + + "auxiliary_fan": "0", + "bed_exclude_area": [ + "0x0" + ], + "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0", + "change_filament_gcode": "", + "cooling_tube_length": "5", + "cooling_tube_retraction": "91.5", + "default_filament_profile": [ + "FlyingBear Generic PLA" + ], + "default_print_profile": "0.20mm Standard @FlyingBear Reborn3", + "deretraction_speed": [ + "40" + ], + "enable_filament_ramming": "1", + "extra_loading_move": "-2", + "extruder_clearance_height_to_lid": "140", + "extruder_clearance_height_to_rod": "36", + "extruder_clearance_radius": "65", + "extruder_colour": [ + "#FCE94F" + ], + "extruder_offset": [ + "0x0" + ], + "fan_kickstart": "0", + "fan_speedup_overhangs": "1", + "fan_speedup_time": "0", + + "high_current_on_filament_swap": "0", + + + "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", + "machine_end_gcode": "PRINT_END", + "machine_load_filament_time": "0", + "machine_max_acceleration_e": [ + "5000", + "5000" + ], + "machine_max_acceleration_extruding": [ + "20000", + "20000" + ], + "machine_max_acceleration_retracting": [ + "5000", + "5000" + ], + "machine_max_acceleration_travel": [ + "9000", + "9000" + ], + "machine_max_acceleration_x": [ + "20000", + "20000" + ], + "machine_max_acceleration_y": [ + "20000", + "20000" + ], + "machine_max_acceleration_z": [ + "30", + "200" + ], + "machine_max_jerk_e": [ + "2.5", + "2.5" + ], + "machine_max_jerk_x": [ + "9", + "9" + ], + "machine_max_jerk_y": [ + "9", + "9" + ], + "machine_max_jerk_z": [ + "2", + "0.4" + ], + "machine_max_speed_e": [ + "15", + "25" + ], + "machine_max_speed_x": [ + "600", + "200" + ], + "machine_max_speed_y": [ + "600", + "200" + ], + "machine_max_speed_z": [ + "5", + "12" + ], + "machine_min_extruding_rate": [ + "0", + "0" + ], + "machine_min_travel_rate": [ + "0", + "0" + ], + "machine_pause_gcode": "PAUSE", +"machine_start_gcode": "M140 S40\nG28\nG4 P200\nG90\nG1 Z4\nZ_TILT_ADJUST\nM140 [bed_temperature_initial_layer_single] \nG90\nG1 X150 Y150 F3000\nG28 Z\nG1 X50 Y-3 F2500\nM109 S[nozzle_temperature_initial_layer]\nM190 S[bed_temperature_initial_layer_single] \nPRINT_START", "machine_unload_filament_time": "0", + "max_layer_height": [ + "0.28" + ], + "min_layer_height": [ + "0.08" + ], + + "nozzle_diameter": [ + "0.4" + ], + "nozzle_hrc": "0", + "nozzle_type": "brass", + "nozzle_volume": "151.32", + "parking_pos_retraction": "92", + + "print_host_webui": "", + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "350", + "printer_model": "Generic Klipper Printer", + "printer_notes": "", + "printer_settings_id": "FlyingBear Reborn3 0.4 nozzle", + "printer_technology": "FFF", + "printer_variant": "0.4", + "printhost_apikey": "", + "printhost_authorization_type": "key", + "printhost_cafile": "", + "printhost_password": "", + "printhost_port": "", + "printhost_ssl_ignore_revoke": "0", + "printhost_user": "", + "purge_in_prime_tower": "1", + "retract_before_wipe": [ + "0%" + ], + "retract_length_toolchange": [ + "0" + ], + "retract_lift_above": [ + "0" + ], + "retract_lift_below": [ + "0" + ], + "retract_lift_enforce": [ + "All Surfaces" + ], + "retract_restart_extra": [ + "0" + ], + "retract_restart_extra_toolchange": [ + "0" + ], + "retract_when_changing_layer": [ + "0" + ], + "retraction_length": [ + "0.5" + ], + "retraction_minimum_travel": [ + "1" + ], + "retraction_speed": [ + "40" + ], + "scan_first_layer": "0", + "silent_mode": "0", + "single_extruder_multi_material": "1", + "template_custom_gcode": "", + "thumbnails": [ + "300x300" + ], + "upward_compatible_machine": [], + "use_firmware_retraction": "0", + "use_relative_e_distances": "1", + "version": "1.6.0.0", + "wipe": [ + "1" + ], + "wipe_distance": [ + "2" + ], + "z_hop": [ + "0" + ], + "z_hop_types": [ + "Normal Lift" + ] + + +} diff --git a/resources/profiles/FlyingBear/machine/fdm_machine_common.json b/resources/profiles/FlyingBear/machine/fdm_machine_common.json new file mode 100644 index 0000000000..ed5640c38d --- /dev/null +++ b/resources/profiles/FlyingBear/machine/fdm_machine_common.json @@ -0,0 +1,206 @@ +{ + "type": "machine", + "name": "fdm_machine_common", + "from": "system", + "instantiation": "false", + "printer_technology": "FFF", + "gcode_flavor": "klipper", + + "auxiliary_fan": "0", + + "bed_exclude_area": [ + "0x0" + ], + "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0", + "change_filament_gcode": "", + "cooling_tube_length": "5", + "cooling_tube_retraction": "91.5", + "default_filament_profile": [ + "FlyingBear Generic PLA" + ], + "default_print_profile": "0.20mm Standard @FlyingBear Reborn3", + "deretraction_speed": [ + "40" + ], + "enable_filament_ramming": "1", + "extra_loading_move": "-2", + "extruder_clearance_height_to_lid": "140", + "extruder_clearance_height_to_rod": "36", + "extruder_clearance_radius": "65", + "extruder_colour": [ + "#FCE94F" + ], + "extruder_offset": [ + "0x0" + ], + "fan_kickstart": "0", + "fan_speedup_overhangs": "1", + "fan_speedup_time": "0", + + + "high_current_on_filament_swap": "0", + + + "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", + "machine_end_gcode": "PRINT_END", + "machine_load_filament_time": "0", + "machine_max_acceleration_e": [ + "5000", + "5000" + ], + "machine_max_acceleration_extruding": [ + "20000", + "20000" + ], + "machine_max_acceleration_retracting": [ + "5000", + "5000" + ], + "machine_max_acceleration_travel": [ + "9000", + "9000" + ], + "machine_max_acceleration_x": [ + "20000", + "20000" + ], + "machine_max_acceleration_y": [ + "20000", + "20000" + ], + "machine_max_acceleration_z": [ + "30", + "200" + ], + "machine_max_jerk_e": [ + "2.5", + "2.5" + ], + "machine_max_jerk_x": [ + "9", + "9" + ], + "machine_max_jerk_y": [ + "9", + "9" + ], + "machine_max_jerk_z": [ + "2", + "0.4" + ], + "machine_max_speed_e": [ + "15", + "25" + ], + "machine_max_speed_x": [ + "600", + "200" + ], + "machine_max_speed_y": [ + "600", + "200" + ], + "machine_max_speed_z": [ + "5", + "12" + ], + "machine_min_extruding_rate": [ + "0", + "0" + ], + "machine_min_travel_rate": [ + "0", + "0" + ], + "machine_pause_gcode": "PAUSE", + "machine_start_gcode": "M140 S40\nG28\nG4 P200\nG90\nG1 Z4\nZ_TILT_ADJUST\nM140 [bed_temperature_initial_layer_single] \nG90\nG1 X150 Y150 F3000\nG28 Z\nG1 X50 Y-3 F2500\nM109 S[nozzle_temperature_initial_layer]\nM190 S[bed_temperature_initial_layer_single] \nPRINT_START", "machine_unload_filament_time": "0", + "max_layer_height": [ + "0.28" + ], + "min_layer_height": [ + "0.08" + ], + + + "nozzle_hrc": "0", + "nozzle_type": "brass", + "nozzle_volume": "151.32", + "parking_pos_retraction": "92", + "print_host_webui": "", + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "350", + "printer_model": "Generic Klipper Printer", + "printer_notes": "", + "printer_settings_id": "FlyingBear Reborn3 0.4 nozzle", + + "printer_variant": "0.4", + "printhost_apikey": "", + "printhost_authorization_type": "key", + "printhost_cafile": "", + "printhost_password": "", + "printhost_port": "", + "printhost_ssl_ignore_revoke": "0", + "printhost_user": "", + "purge_in_prime_tower": "1", + "retract_before_wipe": [ + "0%" + ], + "retract_length_toolchange": [ + "0" + ], + "retract_lift_above": [ + "0" + ], + "retract_lift_below": [ + "0" + ], + "retract_lift_enforce": [ + "All Surfaces" + ], + "retract_restart_extra": [ + "0" + ], + "retract_restart_extra_toolchange": [ + "0" + ], + "retract_when_changing_layer": [ + "0" + ], + "retraction_length": [ + "0.5" + ], + "retraction_minimum_travel": [ + "1" + ], + "retraction_speed": [ + "40" + ], + "scan_first_layer": "0", + "silent_mode": "0", + "single_extruder_multi_material": "1", + "template_custom_gcode": "", + "thumbnails": [ + "300x300" + ], + "upward_compatible_machine": [], + "use_firmware_retraction": "0", + "use_relative_e_distances": "1", + "version": "1.6.0.0", + "wipe": [ + "1" + ], + "wipe_distance": [ + "2" + ], + "z_hop": [ + "0.2" + ], + "z_hop_types": [ + "Normal Lift" + ] +} diff --git a/resources/profiles/FlyingBear/process/0.08mm Extra Fine @FlyingBear Reborn3.json b/resources/profiles/FlyingBear/process/0.08mm Extra Fine @FlyingBear Reborn3.json new file mode 100644 index 0000000000..a4217dfd9b --- /dev/null +++ b/resources/profiles/FlyingBear/process/0.08mm Extra Fine @FlyingBear Reborn3.json @@ -0,0 +1,24 @@ +{ + "type": "process", + "setting_id": "GP004", + "name": "0.08mm Extra Fine @FlyingBear Reborn3", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_common", + + "bottom_shell_layers": "7", + "inner_wall_speed": "350", + "internal_solid_infill_speed": "350", + + "overhang_1_4_speed": "60", + "overhang_2_4_speed": "30", + "overhang_3_4_speed": "10", + "print_settings_id": "0.08mm Standard @FlyingBear Reborn3", + "sparse_infill_speed": "450", + "top_shell_layers": "9", + "top_shell_thickness": "0.8", + "tree_support_wall_count": "1", + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/process/0.12mm Fine @FlyingBear Reborn3.json b/resources/profiles/FlyingBear/process/0.12mm Fine @FlyingBear Reborn3.json new file mode 100644 index 0000000000..d6a6ed697a --- /dev/null +++ b/resources/profiles/FlyingBear/process/0.12mm Fine @FlyingBear Reborn3.json @@ -0,0 +1,27 @@ +{ + "type": "process", + "setting_id": "GP004", + "name": "0.12mm Fine @FlyingBear Reborn3", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_common", + + "bottom_shell_layers": "5", + "brim_width": "5", + "gap_infill_speed": "350", + "inner_wall_speed": "350", + "internal_solid_infill_speed": "350", + "layer_height": "0.12", + "overhang_1_4_speed": "60", + "overhang_2_4_speed": "30", + "overhang_3_4_speed": "10", + "print_settings_id": "0.12mm Standard @FlyingBear Reborn3", + "sparse_infill_speed": "400", + "top_shell_layers": "5", + "top_shell_thickness": "0.6", + "tree_support_wall_count": "0", + "wipe_on_loops": "0", + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/process/0.20mm Standard @FlyingBear Reborn3.json b/resources/profiles/FlyingBear/process/0.20mm Standard @FlyingBear Reborn3.json new file mode 100644 index 0000000000..7ca0b9b414 --- /dev/null +++ b/resources/profiles/FlyingBear/process/0.20mm Standard @FlyingBear Reborn3.json @@ -0,0 +1,35 @@ +{ + "type": "process", + "setting_id": "GP004", + "name": "0.20mm Standard @FlyingBear Reborn3", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_common", + "bottom_shell_layers": "3", + + "brim_width": "5", + "gap_infill_speed": "250", + "infill_wall_overlap": "25%", + "inner_wall_speed": "300", + "internal_solid_infill_speed": "250", + "layer_height": "0.2", + "overhang_1_4_speed": "50", + "overhang_2_4_speed": "50", + "overhang_3_4_speed": "30", + "overhang_speed_classic": "0", + "print_flow_ratio": "0.95", + "print_settings_id": "0.20mm Standard @FlyingBear Reborn3", + "sparse_infill_speed": "270", + "support_line_width": "0.4", + "top_shell_layers": "3", + "top_shell_thickness": "0.6", + "top_surface_line_width": "0.4", + "tree_support_branch_angle_organic": "45", + "tree_support_branch_distance_organic": "3", + "tree_support_wall_count": "1", + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] + + +} diff --git a/resources/profiles/FlyingBear/process/0.24mm Draft @FlyingBear Reborn3.json b/resources/profiles/FlyingBear/process/0.24mm Draft @FlyingBear Reborn3.json new file mode 100644 index 0000000000..99dc4c477a --- /dev/null +++ b/resources/profiles/FlyingBear/process/0.24mm Draft @FlyingBear Reborn3.json @@ -0,0 +1,29 @@ +{ + + "type": "process", + "setting_id": "GP004", + "name": "0.24mm Draft @FlyingBear Reborn3", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_common", + "bottom_shell_layers": "3", + + "brim_width": "3", + "gap_infill_speed": "230", + "inner_wall_speed": "230", + "internal_solid_infill_speed": "230", + "layer_height": "0.24", + "overhang_1_4_speed": "0", + "overhang_2_4_speed": "50", + "overhang_3_4_speed": "30", + "print_settings_id": "0.24mm Draft @FlyingBear Reborn3", + "sparse_infill_speed": "230", + "top_shell_layers": "3", + "top_shell_thickness": "0.6", + "top_surface_line_width": "0.45", + "tree_support_wall_count": "1", + "wipe_on_loops": "0", + "compatible_printers": [ + "FlyingBear Reborn3 0.4 nozzle" + ] +} diff --git a/resources/profiles/FlyingBear/process/fdm_process_common.json b/resources/profiles/FlyingBear/process/fdm_process_common.json new file mode 100644 index 0000000000..982a1b35f2 --- /dev/null +++ b/resources/profiles/FlyingBear/process/fdm_process_common.json @@ -0,0 +1,223 @@ +{ + "type": "process", + "name": "fdm_process_common", + "from": "system", + "instantiation": "false", + + "accel_to_decel_enable": "1", + "accel_to_decel_factor": "50%", + "bottom_shell_thickness": "0", + "bottom_solid_infill_flow_ratio": "1", + "bottom_surface_pattern": "monotonic", + "bridge_acceleration": "50%", + "bridge_angle": "0", + "bridge_density": "100%", + "bridge_flow": "1", + "bridge_no_support": "0", + "bridge_speed": "50", + "brim_ears_detection_length": "1", + "brim_ears_max_angle": "125", + "brim_object_gap": "0.2", + "brim_type": "auto_brim", + "compatible_printers_condition": "", + "default_acceleration": "10000", + "default_jerk": "0", + "detect_narrow_internal_solid_infill": "1", + "detect_overhang_wall": "1", + "detect_thin_wall": "0", + "draft_shield": "disabled", + "elefant_foot_compensation": "0.15", + "enable_arc_fitting": "1", + "enable_overhang_speed": "1", + "enable_prime_tower": "0", + "enable_support": "0", + "enforce_support_layers": "0", + "ensure_vertical_shell_thickness": "1", + "exclude_object": "0", + "extra_perimeters_on_overhangs": "0", + "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filter_out_gap_fill": "0", + "flush_into_infill": "0", + "flush_into_objects": "0", + "flush_into_support": "1", + "fuzzy_skin": "none", + "fuzzy_skin_point_distance": "0.8", + "fuzzy_skin_thickness": "0.3", + + "gcode_add_line_number": "0", + "gcode_comments": "0", + "gcode_label_objects": "0", + "independent_support_layer_height": "1", + "infill_anchor": "400%", + "infill_anchor_max": "20", + "infill_combination": "0", + "infill_direction": "45", + "infill_jerk": "9", + "infill_wall_overlap": "15%", + "initial_layer_acceleration": "500", + "initial_layer_infill_speed": "50", + "initial_layer_jerk": "9", + "initial_layer_line_width": "0.5", + "initial_layer_min_bead_width": "85%", + "initial_layer_print_height": "0.2", + "initial_layer_speed": "30", + "initial_layer_travel_speed": "100%", + "inner_wall_acceleration": "10000", + "inner_wall_jerk": "9", + "inner_wall_line_width": "0.45", + "interface_shells": "0", + "internal_bridge_speed": "150%", + "internal_bridge_support_thickness": "0.8", + "internal_solid_infill_acceleration": "100%", + "internal_solid_infill_line_width": "0.42", + "internal_solid_infill_pattern": "monotonic", + + "ironing_flow": "10%", + "ironing_pattern": "zig-zag", + "ironing_spacing": "0.15", + "ironing_speed": "30", + "ironing_type": "no ironing", + "line_width": "0.42", + "make_overhang_printable": "0", + "make_overhang_printable_angle": "55", + "make_overhang_printable_hole_size": "0", + "max_bridge_length": "10", + "max_travel_detour_distance": "0", + "min_bead_width": "85%", + "min_feature_size": "25%", + "min_width_top_surface": "300%", + "minimum_sparse_infill_area": "15", + "notes": "", + "only_one_wall_first_layer": "0", + "only_one_wall_top": "1", + "ooze_prevention": "0", + "outer_wall_acceleration": "5000", + "outer_wall_jerk": "9", + "outer_wall_line_width": "0.42", + "outer_wall_speed": "200", + + "overhang_4_4_speed": "10", + "overhang_speed_classic": "1", + "post_process": [], + "precise_outer_wall": "1", + "prime_tower_brim_width": "3", + "prime_tower_width": "35", + "prime_volume": "45", + "print_flow_ratio": "1", + "print_sequence": "by layer", + + "raft_contact_distance": "0.1", + "raft_expansion": "1.5", + "raft_first_layer_density": "90%", + "raft_first_layer_expansion": "2", + "raft_layers": "0", + "reduce_crossing_wall": "0", + "reduce_infill_retraction": "1", + "resolution": "0.012", + "role_based_wipe_speed": "1", + "seam_gap": "15%", + "seam_position": "aligned", + "single_extruder_multi_material_priming": "1", + "skirt_distance": "2", + "skirt_height": "1", + "skirt_loops": "0", + "skirt_speed": "0", + "slice_closing_radius": "0.049", + "slicing_mode": "regular", + "slow_down_layers": "0", + "small_perimeter_speed": "50%", + "small_perimeter_threshold": "0", + "solid_infill_filament": "1", + "sparse_infill_acceleration": "100%", + "sparse_infill_density": "15%", + "sparse_infill_filament": "1", + "sparse_infill_line_width": "0.45", + "sparse_infill_pattern": "grid", + + "spiral_mode": "0", + "staggered_inner_seams": "0", + "standby_temperature_delta": "-5", + "support_angle": "0", + "support_base_pattern": "default", + "support_base_pattern_spacing": "2.5", + "support_bottom_interface_spacing": "0.5", + "support_bottom_z_distance": "0.2", + "support_critical_regions_only": "0", + "support_expansion": "0", + "support_filament": "0", + "support_interface_bottom_layers": "2", + "support_interface_filament": "0", + "support_interface_loop_pattern": "0", + "support_interface_pattern": "auto", + "support_interface_spacing": "0.5", + "support_interface_speed": "80", + "support_interface_top_layers": "2", + "support_line_width": "0.42", + "support_object_xy_distance": "0.35", + "support_on_build_plate_only": "0", + "support_remove_small_overhang": "1", + "support_speed": "150", + "support_style": "default", + "support_threshold_angle": "30", + "support_top_z_distance": "0.2", + "support_type": "normal(auto)", + "thick_bridges": "0", + "timelapse_type": "0", + + "top_solid_infill_flow_ratio": "1", + "top_surface_acceleration": "2000", + "top_surface_jerk": "9", + "top_surface_line_width": "0.42", + "top_surface_pattern": "monotonicline", + "top_surface_speed": "200", + "travel_acceleration": "10000", + "travel_jerk": "12", + "travel_speed": "500", + "travel_speed_z": "0", + "tree_support_adaptive_layer_height": "1", + "tree_support_angle_slow": "25", + "tree_support_auto_brim": "0", + "tree_support_branch_angle": "45", + "tree_support_branch_angle_organic": "40", + "tree_support_branch_diameter": "2", + "tree_support_branch_diameter_angle": "5", + "tree_support_branch_diameter_double_wall": "3", + "tree_support_branch_diameter_organic": "2", + "tree_support_branch_distance": "5", + "tree_support_branch_distance_organic": "1", + "tree_support_brim_width": "0", + "tree_support_tip_diameter": "0.8", + "tree_support_top_rate": "30%", + + "version": "1.6.0.0", + "wall_distribution_count": "1", + "wall_filament": "1", + "wall_generator": "classic", + "wall_infill_order": "inner wall/outer wall/infill", + "wall_loops": "2", + "wall_transition_angle": "10", + "wall_transition_filter_deviation": "25%", + "wall_transition_length": "100%", + "wipe_on_loops": "0", + "wipe_speed": "80%", + "wipe_tower_bridging": "10", + "wipe_tower_cone_angle": "0", + "wipe_tower_extra_spacing": "100%", + "wipe_tower_extruder": "0", + "wipe_tower_no_sparse_layers": "0", + "wipe_tower_rotation_angle": "0", + "wiping_volumes_extruders": [ + "70", + "70", + "70", + "70", + "70", + "70", + "70", + "70", + "70", + "70" + ], + "xy_contour_compensation": "0", + "xy_hole_compensation": "0" +} \ No newline at end of file diff --git a/resources/profiles/Folgertech.json b/resources/profiles/Folgertech.json index ac7caaa738..4f03d71bd3 100644 --- a/resources/profiles/Folgertech.json +++ b/resources/profiles/Folgertech.json @@ -1,6 +1,6 @@ { "name": "Folgertech", - "version": "01.06.00.03", + "version": "01.08.00.00", "force_update": "0", "description": "Folgertech configurations", "machine_model_list": [ diff --git a/resources/profiles/Folgertech/process/fdm_process_folgertech_common.json b/resources/profiles/Folgertech/process/fdm_process_folgertech_common.json index 8a37312557..ead894d6f0 100644 --- a/resources/profiles/Folgertech/process/fdm_process_folgertech_common.json +++ b/resources/profiles/Folgertech/process/fdm_process_folgertech_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/InfiMech.json b/resources/profiles/InfiMech.json index bb6778e58c..911dfb5499 100644 --- a/resources/profiles/InfiMech.json +++ b/resources/profiles/InfiMech.json @@ -1,6 +1,6 @@ { "name": "InfiMech", - "version": "01.01.01.01", + "version": "01.08.00.00", "force_update": "1", "description": "InfiMech configurations", "machine_model_list": [ diff --git a/resources/profiles/InfiMech/process/fdm_process_common.json b/resources/profiles/InfiMech/process/fdm_process_common.json index 4b0d75d73d..23eb9cf92e 100644 --- a/resources/profiles/InfiMech/process/fdm_process_common.json +++ b/resources/profiles/InfiMech/process/fdm_process_common.json @@ -35,7 +35,7 @@ "ensure_vertical_shell_thickness": "1", "exclude_object": "0", "extra_perimeters_on_overhangs": "0", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "filter_out_gap_fill": "0", "flush_into_infill": "0", "flush_into_objects": "0", @@ -104,7 +104,6 @@ "prime_tower_brim_width": "3", "prime_tower_width": "35", "prime_volume": "45", - "print_flow_ratio": "0.95", "print_sequence": "by layer", "raft_contact_distance": "0.1", diff --git a/resources/profiles/Kingroon.json b/resources/profiles/Kingroon.json index b83a778881..78eacdbdff 100644 --- a/resources/profiles/Kingroon.json +++ b/resources/profiles/Kingroon.json @@ -1,7 +1,7 @@ { "name": "Kingroon", "url": "https://kingroon.com/", - "version": "01.06.05.00", + "version": "01.08.00.00", "force_update": "0", "description": "Kingroon configuration files", "machine_model_list": [ diff --git a/resources/profiles/Kingroon/process/fdm_process_common.json b/resources/profiles/Kingroon/process/fdm_process_common.json index daf672e289..cfe9b4199d 100644 --- a/resources/profiles/Kingroon/process/fdm_process_common.json +++ b/resources/profiles/Kingroon/process/fdm_process_common.json @@ -35,7 +35,7 @@ "enforce_support_layers": "0", "ensure_vertical_shell_thickness": "1", "exclude_object": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "filter_out_gap_fill": "0", "flush_into_infill": "0", "flush_into_objects": "0", @@ -96,7 +96,6 @@ "prime_tower_brim_width": "3", "prime_tower_width": "60", "prime_volume": "45", - "print_flow_ratio": "1", "print_sequence": "by layer", "print_settings_id": "", "raft_contact_distance": "0.1", diff --git a/resources/profiles/OrcaArena.json b/resources/profiles/OrcaArena.json index bf6cf291a0..c9534b1faf 100644 --- a/resources/profiles/OrcaArena.json +++ b/resources/profiles/OrcaArena.json @@ -1,7 +1,7 @@ { "name": "Orca Arena Printer", "url": "", - "version": "01.06.02.00", + "version": "01.08.00.00", "force_update": "0", "description": "Orca Arena configuration files", "machine_model_list": [ diff --git a/resources/profiles/OrcaArena/process/fdm_process_bbl_common.json b/resources/profiles/OrcaArena/process/fdm_process_bbl_common.json index c5505d4381..67237cea48 100644 --- a/resources/profiles/OrcaArena/process/fdm_process_bbl_common.json +++ b/resources/profiles/OrcaArena/process/fdm_process_bbl_common.json @@ -48,7 +48,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Prusa.json b/resources/profiles/Prusa.json index cd01b944e5..d1e2e0cf35 100644 --- a/resources/profiles/Prusa.json +++ b/resources/profiles/Prusa.json @@ -1,6 +1,6 @@ { "name": "Prusa", - "version": "01.07.01.04", + "version": "01.08.00.00", "force_update": "0", "description": "Prusa configurations", "machine_model_list": [ @@ -30,6 +30,10 @@ "name": "process_common_mk3", "sub_path": "process/process_common_mk3.json" }, + { + "name": "0.20mm Standard @MINI", + "sub_path": "process/0.20mm Standard @MINI.json" + }, { "name": "0.05mm UltraDetail @MK3S", "sub_path": "process/0.05mm UltraDetail @MK3S.json" diff --git a/resources/profiles/Prusa/process/fdm_process_common.json b/resources/profiles/Prusa/process/fdm_process_common.json index 59955bd92b..8aa6ebf563 100644 --- a/resources/profiles/Prusa/process/fdm_process_common.json +++ b/resources/profiles/Prusa/process/fdm_process_common.json @@ -37,7 +37,7 @@ "ironing_speed": "30", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{layer_height}mm_{filament_type[initial_tool]}_{printer_model}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Prusa/process/process_common_mk3.json b/resources/profiles/Prusa/process/process_common_mk3.json index 31e20be3be..6a8dc3a556 100644 --- a/resources/profiles/Prusa/process/process_common_mk3.json +++ b/resources/profiles/Prusa/process/process_common_mk3.json @@ -21,7 +21,6 @@ "enable_support": "0", "enforce_support_layers": "0", "extra_perimeters_on_overhangs": "0", - "filename_format": "{input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode", "fuzzy_skin": "none", "fuzzy_skin_point_distance": "0.8", "fuzzy_skin_thickness": "0.3", diff --git a/resources/profiles/Qidi.json b/resources/profiles/Qidi.json index 3a8f620a4a..4631991b9f 100644 --- a/resources/profiles/Qidi.json +++ b/resources/profiles/Qidi.json @@ -1,6 +1,6 @@ { "name": "Qidi", - "version": "01.07.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Qidi configurations", "machine_model_list": [ diff --git a/resources/profiles/Qidi/process/0.12mm Fine @Qidi X3.json b/resources/profiles/Qidi/process/0.12mm Fine @Qidi X3.json index a4dd816cea..bc2fe0f6ed 100644 --- a/resources/profiles/Qidi/process/0.12mm Fine @Qidi X3.json +++ b/resources/profiles/Qidi/process/0.12mm Fine @Qidi X3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XCFPro.json b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XCFPro.json index 0b94d50cdd..e94ba2bf36 100644 --- a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XCFPro.json +++ b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XCFPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XMax.json b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XMax.json index 9536426778..27cc928993 100644 --- a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XMax.json +++ b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XPlus.json b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XPlus.json index 3de23e3e98..950638c6e8 100644 --- a/resources/profiles/Qidi/process/0.12mm Fine @Qidi XPlus.json +++ b/resources/profiles/Qidi/process/0.12mm Fine @Qidi XPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi X3.json b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi X3.json index a35cf1a4ea..97ecaac2c1 100644 --- a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi X3.json +++ b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi X3.json @@ -33,7 +33,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XCFPro.json b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XCFPro.json index 461982edc6..c3723494a1 100644 --- a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XCFPro.json +++ b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XCFPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XMax.json b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XMax.json index a62caff21c..73d3761633 100644 --- a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XMax.json +++ b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XPlus.json b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XPlus.json index 5121a5ac22..5883e726e0 100644 --- a/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XPlus.json +++ b/resources/profiles/Qidi/process/0.16mm Optimal @Qidi XPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XCFPro.json b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XCFPro.json index c488563d00..978fb331bd 100644 --- a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XCFPro.json +++ b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XCFPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XMax.json b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XMax.json index f6edac7a9b..274a05a25a 100644 --- a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XMax.json +++ b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XPlus.json b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XPlus.json index 33e3771d30..39d83b5b7d 100644 --- a/resources/profiles/Qidi/process/0.20mm Standard @Qidi XPlus.json +++ b/resources/profiles/Qidi/process/0.20mm Standard @Qidi XPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.24mm Draft @Qidi X3.json b/resources/profiles/Qidi/process/0.24mm Draft @Qidi X3.json index d5d77f58f9..90c0637605 100644 --- a/resources/profiles/Qidi/process/0.24mm Draft @Qidi X3.json +++ b/resources/profiles/Qidi/process/0.24mm Draft @Qidi X3.json @@ -31,7 +31,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XCFPro.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XCFPro.json index ceff672b6f..21ed5164fd 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XCFPro.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XCFPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax.json index e6ce6c79bd..6cdb09eb56 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax3.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax3.json index ec19ddf46b..3ab5523afe 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax3.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XMax3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus.json index 023678aaa9..3c9b3735cf 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus3.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus3.json index d96f511d8e..52668072a4 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus3.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XPlus3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XSmart3.json b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XSmart3.json index 5fa3abc3fb..f3d46079f7 100644 --- a/resources/profiles/Qidi/process/0.25mm Draft @Qidi XSmart3.json +++ b/resources/profiles/Qidi/process/0.25mm Draft @Qidi XSmart3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.28mm Extra Draft @Qidi X3.json b/resources/profiles/Qidi/process/0.28mm Extra Draft @Qidi X3.json index 67d529ec32..dd60183d00 100644 --- a/resources/profiles/Qidi/process/0.28mm Extra Draft @Qidi X3.json +++ b/resources/profiles/Qidi/process/0.28mm Extra Draft @Qidi X3.json @@ -31,7 +31,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XCFPro.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XCFPro.json index 121905d28e..7a6af747a9 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XCFPro.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XCFPro.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax.json index 1d89de2046..063964384a 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax3.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax3.json index d5b5aad00f..6b5c01270e 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax3.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XMax3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus.json index 96233b6994..0f3c7fbf6b 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus3.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus3.json index 41f248f8a7..19fec3bcff 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus3.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XPlus3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XSmart3.json b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XSmart3.json index 76d69911d0..a9c78c9216 100644 --- a/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XSmart3.json +++ b/resources/profiles/Qidi/process/0.30mm Extra Draft @Qidi XSmart3.json @@ -36,7 +36,7 @@ "ironing_spacing": "0.1", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "inner_wall_line_width": "0.45", "wall_loops": "2", diff --git a/resources/profiles/Qidi/process/fdm_process_qidi_common.json b/resources/profiles/Qidi/process/fdm_process_qidi_common.json index 1f0df8f0b9..e1fa78fd3d 100644 --- a/resources/profiles/Qidi/process/fdm_process_qidi_common.json +++ b/resources/profiles/Qidi/process/fdm_process_qidi_common.json @@ -42,7 +42,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Qidi/process/fdm_process_qidi_x3_common.json b/resources/profiles/Qidi/process/fdm_process_qidi_x3_common.json index 6f710334de..9116f0f02f 100644 --- a/resources/profiles/Qidi/process/fdm_process_qidi_x3_common.json +++ b/resources/profiles/Qidi/process/fdm_process_qidi_x3_common.json @@ -48,7 +48,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", @@ -114,5 +114,5 @@ "internal_solid_infill_pattern": "monotonic", "initial_layer_travel_speed": "50%", "filter_out_gap_fill": "2", - "notes": "If you want to use Orca's chamber temperature control feature, check that printer.cfg has added the following M191 macro.\nTo add it: go to Fluidd web interface--configuration, copy the following code to the top of the printer.cfg document, SAVE&RESATART \n\n[gcode_macro M191]\ngcode:\n #Parameters\n {% set s = params.S|float %}\n \n M141 {% for p in params %}{'%s%s' % (p, params[p])}{% endfor %} ; Set hotend temp\n {% if s != 0 %}\n TEMPERATURE_WAIT SENSOR=\"heater_generic hot\" MINIMUM={s} MAXIMUM={s+1} ; Wait for hotend temp (within 1 degree)\n {% endif %}" + "notes": "If you want to use Orca's chamber temperature control feature, check that printer.cfg has added the following M191 macro.\nTo add it: go to Fluidd web interface--configuration, copy the following code to the top of the printer.cfg document, SAVE&RESATART \n\n[gcode_macro M191]\ngcode:\n {% set s = params.S|float %}\n {% if s == 0 %}\n # If target temperature is 0, do nothing\n M117 Chamber heating cancelled\n {% else %}\n SET_HEATER_TEMPERATURE HEATER=chamber_heater TARGET={s}\n # Orca: uncomment the following line if you want to use heat bed to assist chamber heating\n M140 S90\n TEMPERATURE_WAIT SENSOR=\"heater_generic chamber_heater\" MINIMUM={s-1} MAXIMUM={s+1}\n M117 Chamber at target temperature\n {% endif %}" } diff --git a/resources/profiles/Raise3D.json b/resources/profiles/Raise3D.json index 6a83b08fcf..07b6d2f77e 100644 --- a/resources/profiles/Raise3D.json +++ b/resources/profiles/Raise3D.json @@ -1,7 +1,7 @@ { "name": "Raise3D", "url": "", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Raise3D configurations", "machine_model_list": [ diff --git a/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3.json b/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3.json index e9da28a15c..0818f0dd88 100644 --- a/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3.json +++ b/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3Plus.json b/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3Plus.json index fa56b86d6f..6150d744c3 100644 --- a/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3Plus.json +++ b/resources/profiles/Raise3D/process/0.10mm Fine @Raise3D Pro3Plus.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3.json b/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3.json index 761f0ee84b..93873d18a4 100644 --- a/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3.json +++ b/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3Plus.json b/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3Plus.json index 70de79e686..9e06abcac5 100644 --- a/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3Plus.json +++ b/resources/profiles/Raise3D/process/0.20mm Standard @Raise3D Pro3Plus.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3.json b/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3.json index 086d324869..8fa0f3696e 100644 --- a/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3.json +++ b/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3Plus.json b/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3Plus.json index d0570bb072..1c5076efc5 100644 --- a/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3Plus.json +++ b/resources/profiles/Raise3D/process/0.25mm Draft @Raise3D Pro3Plus.json @@ -44,7 +44,7 @@ "ironing_speed": "10", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Ratrig.json b/resources/profiles/Ratrig.json index 47dd32d0a6..1790b9c3f5 100644 --- a/resources/profiles/Ratrig.json +++ b/resources/profiles/Ratrig.json @@ -1,6 +1,6 @@ { "name": "RatRig", - "version": "01.07.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "RatRig configurations", "machine_model_list": [ diff --git a/resources/profiles/SecKit.json b/resources/profiles/SecKit.json index 6076ca97de..8998a9c627 100644 --- a/resources/profiles/SecKit.json +++ b/resources/profiles/SecKit.json @@ -1,6 +1,6 @@ { "name": "SecKit", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "SecKit configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker.json b/resources/profiles/Snapmaker.json index 4e63ca07ca..09ecaa09e5 100644 --- a/resources/profiles/Snapmaker.json +++ b/resources/profiles/Snapmaker.json @@ -1,6 +1,6 @@ { "name": "Snapmaker", - "version": "01.05.04.01", + "version": "01.08.00.00", "force_update": "0", "description": "Snapmaker configurations", "machine_model_list": [ diff --git a/resources/profiles/Snapmaker/machine/Snapmaker J1.json b/resources/profiles/Snapmaker/machine/Snapmaker J1.json index 019ba7251e..d08865471c 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker J1.json +++ b/resources/profiles/Snapmaker/machine/Snapmaker J1.json @@ -2,7 +2,7 @@ "type": "machine_model", "name": "Snapmaker J1", "model_id": "SnapmakerJ1", - "nozzle_diameter": "0.4;0,6", + "nozzle_diameter": "0.4;0.6", "machine_tech": "FFF", "family": "Snapmaker", "bed_model": "", diff --git a/resources/profiles/Snapmaker/process/fdm_process_common.json b/resources/profiles/Snapmaker/process/fdm_process_common.json index 6cfb587794..3e9f1c8d19 100644 --- a/resources/profiles/Snapmaker/process/fdm_process_common.json +++ b/resources/profiles/Snapmaker/process/fdm_process_common.json @@ -40,7 +40,7 @@ "ironing_speed": "30", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Sovol.json b/resources/profiles/Sovol.json index 0daf7f39ad..29e68818db 100644 --- a/resources/profiles/Sovol.json +++ b/resources/profiles/Sovol.json @@ -1,7 +1,7 @@ { "name": "Sovol", "url": "", - "version": "01.00.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Sovol configurations", "machine_model_list": [ diff --git a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV01Pro.json b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV01Pro.json index 7f09b70b75..7e4a1b232c 100644 --- a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV01Pro.json +++ b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV01Pro.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV02.json b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV02.json index 15b7aee7c9..f598f0464c 100644 --- a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV02.json +++ b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV02.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV05.json b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV05.json index 1fca86538d..f6686c5718 100644 --- a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV05.json +++ b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV05.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06.json b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06.json index 3b03120166..5ed34435b4 100644 --- a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06.json +++ b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06Plus.json b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06Plus.json index f3319adbe1..a98e47d432 100644 --- a/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06Plus.json +++ b/resources/profiles/Sovol/process/0.18mm Optimal @Sovol SV06Plus.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV01Pro.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV01Pro.json index 0d8f104384..e9fa54468c 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV01Pro.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV01Pro.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV02.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV02.json index c65d2850bc..dd0b5b9f21 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV02.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV02.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV05.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV05.json index e448cad3aa..8a5661ad38 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV05.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV05.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06.json index e65bf6972e..ae293d4841 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06Plus.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06Plus.json index 3a21a9f148..2859ad0ed5 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06Plus.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol SV06Plus.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Tronxy.json b/resources/profiles/Tronxy.json index 481a1f9dae..c88d53fe24 100644 --- a/resources/profiles/Tronxy.json +++ b/resources/profiles/Tronxy.json @@ -1,6 +1,6 @@ { "name": "Tronxy", - "version": "01.06.00.1", + "version": "01.08.00.00", "force_update": "0", "description": "Tronxy configurations", "machine_model_list": [ diff --git a/resources/profiles/TwoTrees.json b/resources/profiles/TwoTrees.json index 59193a7129..c65f4415e7 100644 --- a/resources/profiles/TwoTrees.json +++ b/resources/profiles/TwoTrees.json @@ -1,6 +1,6 @@ { "name": "TwoTrees", - "version": "01.06.01.00", + "version": "01.08.00.00", "force_update": "1", "description": "TwoTrees configurations", "machine_model_list": [ diff --git a/resources/profiles/UltiMaker.json b/resources/profiles/UltiMaker.json index e0f88c0110..3033a96906 100644 --- a/resources/profiles/UltiMaker.json +++ b/resources/profiles/UltiMaker.json @@ -1,7 +1,7 @@ { "name": "UltiMaker", "url": "", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "UltiMaker configurations", "machine_model_list": [ diff --git a/resources/profiles/UltiMaker/process/0.12mm Fine @UltiMaker 2.json b/resources/profiles/UltiMaker/process/0.12mm Fine @UltiMaker 2.json index 0275cc8352..567a228c3c 100644 --- a/resources/profiles/UltiMaker/process/0.12mm Fine @UltiMaker 2.json +++ b/resources/profiles/UltiMaker/process/0.12mm Fine @UltiMaker 2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/UltiMaker/process/0.18mm Standard @UltiMaker 2.json b/resources/profiles/UltiMaker/process/0.18mm Standard @UltiMaker 2.json index bf8a9e4442..6e6829860f 100644 --- a/resources/profiles/UltiMaker/process/0.18mm Standard @UltiMaker 2.json +++ b/resources/profiles/UltiMaker/process/0.18mm Standard @UltiMaker 2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/UltiMaker/process/0.25mm Darft @UltiMaker 2.json b/resources/profiles/UltiMaker/process/0.25mm Darft @UltiMaker 2.json index fb5ea065ab..5740c576b5 100644 --- a/resources/profiles/UltiMaker/process/0.25mm Darft @UltiMaker 2.json +++ b/resources/profiles/UltiMaker/process/0.25mm Darft @UltiMaker 2.json @@ -44,7 +44,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Vivedino.json b/resources/profiles/Vivedino.json index 1471f9e416..58b1a46495 100644 --- a/resources/profiles/Vivedino.json +++ b/resources/profiles/Vivedino.json @@ -1,6 +1,6 @@ { "name": "Vivedino", - "version": "01.06.04.01", + "version": "01.08.00.00", "force_update": "0", "description": "Vivedino configurations", "machine_model_list": [ diff --git a/resources/profiles/Vivedino/process/fdm_process_klipper_common.json b/resources/profiles/Vivedino/process/fdm_process_klipper_common.json index 19176b5504..0473ee3521 100644 --- a/resources/profiles/Vivedino/process/fdm_process_klipper_common.json +++ b/resources/profiles/Vivedino/process/fdm_process_klipper_common.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Voron.json b/resources/profiles/Voron.json index 1dcaf31a5d..4f3ea971ef 100644 --- a/resources/profiles/Voron.json +++ b/resources/profiles/Voron.json @@ -1,6 +1,6 @@ { "name": "Voron", - "version": "01.06.05.03", + "version": "01.08.00.00", "force_update": "0", "description": "Voron configurations", "machine_model_list": [ diff --git a/resources/profiles/Voron/process/fdm_process_voron_common.json b/resources/profiles/Voron/process/fdm_process_voron_common.json index d242c947ed..d993c7b5ea 100644 --- a/resources/profiles/Voron/process/fdm_process_voron_common.json +++ b/resources/profiles/Voron/process/fdm_process_voron_common.json @@ -44,7 +44,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "80%", "overhang_2_4_speed": "50", diff --git a/resources/profiles/Voxelab.json b/resources/profiles/Voxelab.json index 053f250506..6370da51ab 100644 --- a/resources/profiles/Voxelab.json +++ b/resources/profiles/Voxelab.json @@ -1,7 +1,7 @@ { "name": "Voxelab", "url": "", - "version": "01.06.00.00", + "version": "01.08.00.00", "force_update": "0", "description": "Voxelab configurations", "machine_model_list": [ diff --git a/resources/profiles/Voxelab/process/0.16mm Optimal @Voxelab AquilaX2.json b/resources/profiles/Voxelab/process/0.16mm Optimal @Voxelab AquilaX2.json index 6f0a07e2c6..6aa62f184d 100644 --- a/resources/profiles/Voxelab/process/0.16mm Optimal @Voxelab AquilaX2.json +++ b/resources/profiles/Voxelab/process/0.16mm Optimal @Voxelab AquilaX2.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Voxelab/process/0.20mm Standard @Voxelab AquilaX2.json b/resources/profiles/Voxelab/process/0.20mm Standard @Voxelab AquilaX2.json index 029a0a33e8..db2d6f6595 100644 --- a/resources/profiles/Voxelab/process/0.20mm Standard @Voxelab AquilaX2.json +++ b/resources/profiles/Voxelab/process/0.20mm Standard @Voxelab AquilaX2.json @@ -45,7 +45,7 @@ "ironing_speed": "15", "ironing_type": "no ironing", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/profiles/Vzbot.json b/resources/profiles/Vzbot.json index de86c5aa30..08f819e004 100644 --- a/resources/profiles/Vzbot.json +++ b/resources/profiles/Vzbot.json @@ -1,6 +1,6 @@ { "name": "Vzbot", - "version": "01.06.04.00", + "version": "01.08.00.00", "force_update": "0", "description": "Vzbot configurations", "machine_model_list": [ diff --git a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common.json b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common.json index bf6689508d..394d77048e 100644 --- a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common.json +++ b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common.json @@ -45,7 +45,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "120", diff --git a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.5_nozzle.json b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.5_nozzle.json index c496eb764b..5e5a80da0b 100644 --- a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.5_nozzle.json +++ b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.5_nozzle.json @@ -45,7 +45,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "120", diff --git a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.6_nozzle.json b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.6_nozzle.json index b3d54e197e..eedeb3fd31 100644 --- a/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.6_nozzle.json +++ b/resources/profiles/Vzbot/process/fdm_process_Vzbot_common_0.6_nozzle.json @@ -45,7 +45,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "120", diff --git a/resources/profiles/Wanhao.json b/resources/profiles/Wanhao.json index c21f59864a..e4b43d2684 100644 --- a/resources/profiles/Wanhao.json +++ b/resources/profiles/Wanhao.json @@ -1,6 +1,6 @@ { "name": "Wanhao", - "version": "01.00.01.00", + "version": "01.08.00.00", "force_update": "0", "description": "Wanhao configurations", "machine_model_list": [ diff --git a/resources/profiles/Wanhao/process/fdm_process_wanhao_common.json b/resources/profiles/Wanhao/process/fdm_process_wanhao_common.json index 29c64af2a4..bbb877de8f 100644 --- a/resources/profiles/Wanhao/process/fdm_process_wanhao_common.json +++ b/resources/profiles/Wanhao/process/fdm_process_wanhao_common.json @@ -43,7 +43,7 @@ "ironing_type": "no ironing", "layer_height": "0.2", "reduce_infill_retraction": "1", - "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "filename_format": "{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode", "detect_overhang_wall": "1", "overhang_1_4_speed": "0", "overhang_2_4_speed": "20", diff --git a/resources/shaders/110/background.fs b/resources/shaders/110/background.fs new file mode 100644 index 0000000000..b148440898 --- /dev/null +++ b/resources/shaders/110/background.fs @@ -0,0 +1,11 @@ +#version 110 + +uniform vec4 top_color; +uniform vec4 bottom_color; + +varying vec2 tex_coord; + +void main() +{ + gl_FragColor = mix(bottom_color, top_color, tex_coord.y); +} diff --git a/resources/shaders/110/background.vs b/resources/shaders/110/background.vs new file mode 100644 index 0000000000..9b56ab43a2 --- /dev/null +++ b/resources/shaders/110/background.vs @@ -0,0 +1,12 @@ +#version 110 + +attribute vec3 v_position; +attribute vec2 v_tex_coord; + +varying vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = vec4(v_position, 1.0); +} diff --git a/resources/shaders/cali.fs b/resources/shaders/110/flat.fs similarity index 100% rename from resources/shaders/cali.fs rename to resources/shaders/110/flat.fs diff --git a/resources/shaders/110/flat.vs b/resources/shaders/110/flat.vs new file mode 100644 index 0000000000..d9063f0c70 --- /dev/null +++ b/resources/shaders/110/flat.vs @@ -0,0 +1,11 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +attribute vec3 v_position; + +void main() +{ + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/110/flat_clip.fs b/resources/shaders/110/flat_clip.fs new file mode 100644 index 0000000000..ececb8eb1a --- /dev/null +++ b/resources/shaders/110/flat_clip.fs @@ -0,0 +1,15 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform vec4 uniform_color; + +varying vec3 clipping_planes_dots; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + gl_FragColor = uniform_color; +} diff --git a/resources/shaders/110/flat_clip.vs b/resources/shaders/110/flat_clip.vs new file mode 100644 index 0000000000..cdf7d4b3b2 --- /dev/null +++ b/resources/shaders/110/flat_clip.vs @@ -0,0 +1,23 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; + +varying vec3 clipping_planes_dots; + +void main() +{ + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0); + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/110/flat_texture.fs b/resources/shaders/110/flat_texture.fs new file mode 100644 index 0000000000..ffe193b1c0 --- /dev/null +++ b/resources/shaders/110/flat_texture.fs @@ -0,0 +1,10 @@ +#version 110 + +uniform sampler2D uniform_texture; + +varying vec2 tex_coord; + +void main() +{ + gl_FragColor = texture2D(uniform_texture, tex_coord); +} diff --git a/resources/shaders/110/flat_texture.vs b/resources/shaders/110/flat_texture.vs new file mode 100644 index 0000000000..dc4868b04d --- /dev/null +++ b/resources/shaders/110/flat_texture.vs @@ -0,0 +1,15 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +attribute vec3 v_position; +attribute vec2 v_tex_coord; + +varying vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/110/gouraud.fs b/resources/shaders/110/gouraud.fs new file mode 100644 index 0000000000..6f354ff9a6 --- /dev/null +++ b/resources/shaders/110/gouraud.fs @@ -0,0 +1,107 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +//BBS: add grey and orange +//const vec3 GREY = vec3(0.9, 0.9, 0.9); +const vec3 ORANGE = vec3(0.8, 0.4, 0.0); +const vec3 LightRed = vec3(0.78, 0.0, 0.0); +const vec3 LightBlue = vec3(0.73, 1.0, 1.0); +const float EPSILON = 0.0001; + +struct PrintVolumeDetection +{ + // 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid + int type; + // type = 0 (rectangle): + // x = min.x, y = min.y, z = max.x, w = max.y + // type = 1 (circle): + // x = center.x, y = center.y, z = radius + vec4 xy_data; + // x = min z, y = max z + vec2 z_data; +}; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform vec4 uniform_color; +uniform bool use_color_clip_plane; +uniform vec4 uniform_color_clip_plane_1; +uniform vec4 uniform_color_clip_plane_2; +uniform SlopeDetection slope; + +//BBS: add outline_color +uniform bool is_outline; + +#ifdef ENABLE_ENVIRONMENT_MAP + uniform sampler2D environment_tex; + uniform bool use_environment_tex; +#endif // ENABLE_ENVIRONMENT_MAP + +uniform PrintVolumeDetection print_volume; + +varying vec3 clipping_planes_dots; +varying float color_clip_plane_dot; + +// x = diffuse, y = specular; +varying vec2 intensity; + +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec4 color; + if (use_color_clip_plane) { + color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb; + color.a = uniform_color.a; + } + else + color = uniform_color; + + if (slope.actived) { + if(world_pos.z<0.1&&world_pos.z>-0.1) + { + color.rgb = LightBlue; + color.a = 0.8; + } + else if( world_normal_z < slope.normal_z - EPSILON) + { + color.rgb = color.rgb * 0.5 + LightRed * 0.5; + color.a = 0.8; + } + } + // if the fragment is outside the print volume -> use darker color + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + // rectangle + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + } + else if (print_volume.type == 1) { + // circle + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + } + color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; + + //BBS: add outline_color + if (is_outline) + gl_FragColor = uniform_color; +#ifdef ENABLE_ENVIRONMENT_MAP + else if (use_environment_tex) + gl_FragColor = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); +#endif + else + gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); +} \ No newline at end of file diff --git a/resources/shaders/110/gouraud.vs b/resources/shaders/110/gouraud.vs new file mode 100644 index 0000000000..ff3a190c79 --- /dev/null +++ b/resources/shaders/110/gouraud.vs @@ -0,0 +1,81 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform SlopeDetection slope; + +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; +// Color clip plane - general orientation. Used by the cut gizmo. +uniform vec4 color_clip_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = diffuse, y = specular; +varying vec2 intensity; + +varying vec3 clipping_planes_dots; +varying float color_clip_plane_dot; + +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +void main() +{ + // First transform the normal into camera space and normalize the result. + eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + world_pos = volume_world_matrix * vec4(v_position, 1.0); + + // z component of normal vector in world coordinate used for slope shading + world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0; + + gl_Position = projection_matrix * position; + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + color_clip_plane_dot = dot(world_pos, color_clip_plane); +} diff --git a/resources/shaders/110/gouraud_light.fs b/resources/shaders/110/gouraud_light.fs new file mode 100644 index 0000000000..970185a00e --- /dev/null +++ b/resources/shaders/110/gouraud_light.fs @@ -0,0 +1,12 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/110/gouraud_light.vs b/resources/shaders/110/gouraud_light.vs new file mode 100644 index 0000000000..135a23b3da --- /dev/null +++ b/resources/shaders/110/gouraud_light.vs @@ -0,0 +1,45 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/110/gouraud_light_instanced.fs b/resources/shaders/110/gouraud_light_instanced.fs new file mode 100644 index 0000000000..970185a00e --- /dev/null +++ b/resources/shaders/110/gouraud_light_instanced.fs @@ -0,0 +1,12 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/110/gouraud_light_instanced.vs b/resources/shaders/110/gouraud_light_instanced.vs new file mode 100644 index 0000000000..f512a9cafc --- /dev/null +++ b/resources/shaders/110/gouraud_light_instanced.vs @@ -0,0 +1,50 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; + +// vertex attributes +attribute vec3 v_position; +attribute vec3 v_normal; +// instance attributes +attribute vec3 i_offset; +attribute vec2 i_scales; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 world_position = vec4(v_position * vec3(vec2(1.5 * i_scales.x), 1.5 * i_scales.y) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0); + vec4 eye_position = view_model_matrix * world_position; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; +} diff --git a/resources/shaders/110/imgui.fs b/resources/shaders/110/imgui.fs new file mode 100644 index 0000000000..4b0e27ce9d --- /dev/null +++ b/resources/shaders/110/imgui.fs @@ -0,0 +1,11 @@ +#version 110 + +uniform sampler2D Texture; + +varying vec2 Frag_UV; +varying vec4 Frag_Color; + +void main() +{ + gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st); +} \ No newline at end of file diff --git a/resources/shaders/110/imgui.vs b/resources/shaders/110/imgui.vs new file mode 100644 index 0000000000..100813e2b5 --- /dev/null +++ b/resources/shaders/110/imgui.vs @@ -0,0 +1,17 @@ +#version 110 + +uniform mat4 ProjMtx; + +attribute vec2 Position; +attribute vec2 UV; +attribute vec4 Color; + +varying vec2 Frag_UV; +varying vec4 Frag_Color; + +void main() +{ + Frag_UV = UV; + Frag_Color = Color; + gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/resources/shaders/options_110.fs b/resources/shaders/110/mm_contour.fs similarity index 100% rename from resources/shaders/options_110.fs rename to resources/shaders/110/mm_contour.fs diff --git a/resources/shaders/110/mm_contour.vs b/resources/shaders/110/mm_contour.vs new file mode 100644 index 0000000000..b37394b619 --- /dev/null +++ b/resources/shaders/110/mm_contour.vs @@ -0,0 +1,15 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform float offset; + +attribute vec3 v_position; + +void main() +{ + // Add small epsilon to z to solve z-fighting between painted triangles and contour lines. + vec4 clip_position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); + clip_position.z -= offset * abs(clip_position.w); + gl_Position = clip_position; +} diff --git a/resources/shaders/110/mm_gouraud.fs b/resources/shaders/110/mm_gouraud.fs new file mode 100644 index 0000000000..8ca23df66d --- /dev/null +++ b/resources/shaders/110/mm_gouraud.fs @@ -0,0 +1,90 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float EPSILON = 0.0001; +//BBS: add grey and orange +//const vec3 GREY = vec3(0.9, 0.9, 0.9); +const vec3 ORANGE = vec3(0.8, 0.4, 0.0); +const vec3 LightRed = vec3(0.78, 0.0, 0.0); +const vec3 LightBlue = vec3(0.73, 1.0, 1.0); +uniform vec4 uniform_color; + +uniform bool volume_mirrored; + +uniform mat4 view_model_matrix; +uniform mat3 view_normal_matrix; + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; +uniform SlopeDetection slope; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + vec3 color = uniform_color.rgb; + float alpha = uniform_color.a; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); +#ifdef FLIP_TRIANGLE_NORMALS + triangle_normal = -triangle_normal; +#endif + + if (volume_mirrored) + triangle_normal = -triangle_normal; + + vec3 transformed_normal = normalize(slope.volume_world_normal_matrix * triangle_normal); + + if (slope.actived) { + if(world_pos.z<0.1&&world_pos.z>-0.1) + { + color = LightBlue; + alpha = 1.0; + } + else if( transformed_normal.z < slope.normal_z - EPSILON) + { + color = color * 0.5 + LightRed * 0.5; + alpha = 1.0; + } + } + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + // x = diffuse, y = specular; + vec2 intensity = vec2(0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (view_model_matrix * model_pos).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha); +} diff --git a/resources/shaders/110/mm_gouraud.vs b/resources/shaders/110/mm_gouraud.vs new file mode 100644 index 0000000000..e8c679b6bf --- /dev/null +++ b/resources/shaders/110/mm_gouraud.vs @@ -0,0 +1,35 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +uniform mat4 volume_world_matrix; +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; + +varying vec3 clipping_planes_dots; +varying vec4 model_pos; +varying vec4 world_pos; +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; +uniform SlopeDetection slope; +void main() +{ + model_pos = vec4(v_position, 1.0); + // Point in homogenous coordinates. + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); +} diff --git a/resources/shaders/110/printbed.fs b/resources/shaders/110/printbed.fs new file mode 100644 index 0000000000..833dff08f4 --- /dev/null +++ b/resources/shaders/110/printbed.fs @@ -0,0 +1,34 @@ +#version 110 + +const vec3 back_color_dark = vec3(0.235, 0.235, 0.235); +const vec3 back_color_light = vec3(0.365, 0.365, 0.365); + +uniform sampler2D texture; +uniform bool transparent_background; +uniform bool svg_source; + +varying vec2 tex_coord; + +vec4 svg_color() +{ + // takes foreground from texture + vec4 fore_color = texture2D(texture, tex_coord); + + // calculates radial gradient + vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coord.xy) - vec2(0.5))))); + + // blends foreground with background + return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); +} + +vec4 non_svg_color() +{ + // takes foreground from texture + vec4 color = texture2D(texture, tex_coord); + return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a); +} + +void main() +{ + gl_FragColor = svg_source ? svg_color() : non_svg_color(); +} \ No newline at end of file diff --git a/resources/shaders/110/printbed.vs b/resources/shaders/110/printbed.vs new file mode 100644 index 0000000000..dc4868b04d --- /dev/null +++ b/resources/shaders/110/printbed.vs @@ -0,0 +1,15 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +attribute vec3 v_position; +attribute vec2 v_tex_coord; + +varying vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/110/thumbnail.fs b/resources/shaders/110/thumbnail.fs new file mode 100644 index 0000000000..4b269734ee --- /dev/null +++ b/resources/shaders/110/thumbnail.fs @@ -0,0 +1,16 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; +//varying float drop; +varying vec4 world_pos; + +void main() +{ + if (world_pos.z < 0.0) + discard; + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/110/thumbnail.vs b/resources/shaders/110/thumbnail.vs new file mode 100644 index 0000000000..69fd0c753c --- /dev/null +++ b/resources/shaders/110/thumbnail.vs @@ -0,0 +1,50 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; +varying vec4 world_pos; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + world_pos = volume_world_matrix * vec4(v_position, 1.0); + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/110/variable_layer_height.fs b/resources/shaders/110/variable_layer_height.fs new file mode 100644 index 0000000000..693c1c6a0b --- /dev/null +++ b/resources/shaders/110/variable_layer_height.fs @@ -0,0 +1,41 @@ +#version 110 + +#define M_PI 3.1415926535897932384626433832795 + +// 2D texture (1D texture split by the rows) of color along the object Z axis. +uniform sampler2D z_texture; +// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. +uniform float z_to_texture_row; +uniform float z_texture_row_to_normalized; +uniform float z_cursor; +uniform float z_cursor_band_width; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float object_z; + +void main() +{ + float object_z_row = z_to_texture_row * object_z; + // Index of the row in the texture. + float z_texture_row = floor(object_z_row); + // Normalized coordinate from 0. to 1. + float z_texture_col = object_z_row - z_texture_row; + float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; + // Calculate level of detail from the object Z coordinate. + // This makes the slowly sloping surfaces to be shown with high detail (with stripes), + // and the vertical surfaces to be shown with low detail (no stripes) + float z_in_cells = object_z_row * 190.; + // Gradient of Z projected on the screen. + float dx_vtc = dFdx(z_in_cells); + float dy_vtc = dFdy(z_in_cells); + float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.); + // Sample the Z texture. Texture coordinates are normalized to <0, 1>. + vec4 color = vec4(0.25, 0.25, 0.25, 1.0); + if (z_texture_row >= 0.0) + color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), + texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); + // Mix the final color. + gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); +} diff --git a/resources/shaders/110/variable_layer_height.vs b/resources/shaders/110/variable_layer_height.vs new file mode 100644 index 0000000000..2d6334f44e --- /dev/null +++ b/resources/shaders/110/variable_layer_height.vs @@ -0,0 +1,60 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform float object_max_z; + +attribute vec3 v_position; +attribute vec3 v_normal; +attribute vec2 v_tex_coord; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float object_z; + +void main() +{ + // ===================================================== + // NOTE: + // when object_max_z > 0.0 we are rendering the overlay + // when object_max_z == 0.0 we are rendering the volumes + // ===================================================== + + // First transform the normal into camera space and normalize the result. + vec3 normal = (object_max_z > 0.0) ? vec3(0.0, 0.0, 1.0) : normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular) + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Scaled to widths of the Z texture. + object_z = (object_max_z > 0.0) ? object_max_z * v_tex_coord.y : (volume_world_matrix * vec4(v_position, 1.0)).z; + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/140/background.fs b/resources/shaders/140/background.fs new file mode 100644 index 0000000000..c21f3a70cd --- /dev/null +++ b/resources/shaders/140/background.fs @@ -0,0 +1,11 @@ +#version 140 + +uniform vec4 top_color; +uniform vec4 bottom_color; + +in vec2 tex_coord; + +void main() +{ + gl_FragColor = mix(bottom_color, top_color, tex_coord.y); +} diff --git a/resources/shaders/140/background.vs b/resources/shaders/140/background.vs new file mode 100644 index 0000000000..13609b3a21 --- /dev/null +++ b/resources/shaders/140/background.vs @@ -0,0 +1,12 @@ +#version 140 + +in vec3 v_position; +in vec2 v_tex_coord; + +out vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/flat.fs b/resources/shaders/140/flat.fs new file mode 100644 index 0000000000..e74124dcae --- /dev/null +++ b/resources/shaders/140/flat.fs @@ -0,0 +1,8 @@ +#version 140 + +uniform vec4 uniform_color; + +void main() +{ + gl_FragColor = uniform_color; +} diff --git a/resources/shaders/140/flat.vs b/resources/shaders/140/flat.vs new file mode 100644 index 0000000000..7042671de2 --- /dev/null +++ b/resources/shaders/140/flat.vs @@ -0,0 +1,11 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +in vec3 v_position; + +void main() +{ + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/flat_clip.fs b/resources/shaders/140/flat_clip.fs new file mode 100644 index 0000000000..b77e0bfaa6 --- /dev/null +++ b/resources/shaders/140/flat_clip.fs @@ -0,0 +1,17 @@ +#version 140 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform vec4 uniform_color; + +in vec3 clipping_planes_dots; + +out vec4 out_color; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + out_color = uniform_color; +} diff --git a/resources/shaders/140/flat_clip.vs b/resources/shaders/140/flat_clip.vs new file mode 100644 index 0000000000..40cddf1e5a --- /dev/null +++ b/resources/shaders/140/flat_clip.vs @@ -0,0 +1,23 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +in vec3 v_position; + +out vec3 clipping_planes_dots; + +void main() +{ + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0); + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/flat_texture.fs b/resources/shaders/140/flat_texture.fs new file mode 100644 index 0000000000..dec946721e --- /dev/null +++ b/resources/shaders/140/flat_texture.fs @@ -0,0 +1,10 @@ +#version 140 + +uniform sampler2D uniform_texture; + +in vec2 tex_coord; + +void main() +{ + gl_FragColor = texture(uniform_texture, tex_coord); +} diff --git a/resources/shaders/140/flat_texture.vs b/resources/shaders/140/flat_texture.vs new file mode 100644 index 0000000000..57d8ca3b7c --- /dev/null +++ b/resources/shaders/140/flat_texture.vs @@ -0,0 +1,15 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +in vec3 v_position; +in vec2 v_tex_coord; + +out vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/gouraud.fs b/resources/shaders/140/gouraud.fs new file mode 100644 index 0000000000..84bce5c035 --- /dev/null +++ b/resources/shaders/140/gouraud.fs @@ -0,0 +1,109 @@ +#version 140 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +//BBS: add grey and orange +//const vec3 GREY = vec3(0.9, 0.9, 0.9); +const vec3 ORANGE = vec3(0.8, 0.4, 0.0); +const vec3 LightRed = vec3(0.78, 0.0, 0.0); +const vec3 LightBlue = vec3(0.73, 1.0, 1.0); +const float EPSILON = 0.0001; + +struct PrintVolumeDetection +{ + // 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid + int type; + // type = 0 (rectangle): + // x = min.x, y = min.y, z = max.x, w = max.y + // type = 1 (circle): + // x = center.x, y = center.y, z = radius + vec4 xy_data; + // x = min z, y = max z + vec2 z_data; +}; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform vec4 uniform_color; +uniform bool use_color_clip_plane; +uniform vec4 uniform_color_clip_plane_1; +uniform vec4 uniform_color_clip_plane_2; +uniform SlopeDetection slope; + +//BBS: add outline_color +uniform bool is_outline; + +#ifdef ENABLE_ENVIRONMENT_MAP + uniform sampler2D environment_tex; + uniform bool use_environment_tex; +#endif // ENABLE_ENVIRONMENT_MAP + +uniform PrintVolumeDetection print_volume; + +in vec3 clipping_planes_dots; +in float color_clip_plane_dot; + +// x = diffuse, y = specular; +in vec2 intensity; + +in vec4 world_pos; +in float world_normal_z; +in vec3 eye_normal; + +out vec4 out_color; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec4 color; + if (use_color_clip_plane) { + color.rgb = (color_clip_plane_dot < 0.0) ? uniform_color_clip_plane_1.rgb : uniform_color_clip_plane_2.rgb; + color.a = uniform_color.a; + } + else + color = uniform_color; + + if (slope.actived) { + if(world_pos.z<0.1&&world_pos.z>-0.1) + { + color.rgb = LightBlue; + color.a = 0.8; + } + else if( world_normal_z < slope.normal_z - EPSILON) + { + color.rgb = color.rgb * 0.5 + LightRed * 0.5; + color.a = 0.8; + } + } + // if the fragment is outside the print volume -> use darker color + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + // rectangle + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + } + else if (print_volume.type == 1) { + // circle + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + } + color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; + + //BBS: add outline_color + if (is_outline) + out_color = uniform_color; +#ifdef ENABLE_ENVIRONMENT_MAP + else if (use_environment_tex) + out_color = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); +#endif + else + out_color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); +} \ No newline at end of file diff --git a/resources/shaders/140/gouraud.vs b/resources/shaders/140/gouraud.vs new file mode 100644 index 0000000000..72dd4ff1bf --- /dev/null +++ b/resources/shaders/140/gouraud.vs @@ -0,0 +1,81 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform SlopeDetection slope; + +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; +// Color clip plane - general orientation. Used by the cut gizmo. +uniform vec4 color_clip_plane; + +in vec3 v_position; +in vec3 v_normal; + +// x = diffuse, y = specular; +out vec2 intensity; + +out vec3 clipping_planes_dots; +out float color_clip_plane_dot; + +out vec4 world_pos; +out float world_normal_z; +out vec3 eye_normal; + +void main() +{ + // First transform the normal into camera space and normalize the result. + eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + world_pos = volume_world_matrix * vec4(v_position, 1.0); + + // z component of normal vector in world coordinate used for slope shading + world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0; + + gl_Position = projection_matrix * position; + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + color_clip_plane_dot = dot(world_pos, color_clip_plane); +} diff --git a/resources/shaders/140/gouraud_light.fs b/resources/shaders/140/gouraud_light.fs new file mode 100644 index 0000000000..de616e066a --- /dev/null +++ b/resources/shaders/140/gouraud_light.fs @@ -0,0 +1,12 @@ +#version 140 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +in vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/140/gouraud_light.vs b/resources/shaders/140/gouraud_light.vs new file mode 100644 index 0000000000..fad848f8bd --- /dev/null +++ b/resources/shaders/140/gouraud_light.vs @@ -0,0 +1,45 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; + +in vec3 v_position; +in vec3 v_normal; + +// x = tainted, y = specular; +out vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/140/gouraud_light_instanced.fs b/resources/shaders/140/gouraud_light_instanced.fs new file mode 100644 index 0000000000..de616e066a --- /dev/null +++ b/resources/shaders/140/gouraud_light_instanced.fs @@ -0,0 +1,12 @@ +#version 140 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +in vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/140/gouraud_light_instanced.vs b/resources/shaders/140/gouraud_light_instanced.vs new file mode 100644 index 0000000000..e0437ca397 --- /dev/null +++ b/resources/shaders/140/gouraud_light_instanced.vs @@ -0,0 +1,50 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; + +// vertex attributes +in vec3 v_position; +in vec3 v_normal; +// instance attributes +in vec3 i_offset; +in vec2 i_scales; + +// x = tainted, y = specular; +out vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 world_position = vec4(v_position * vec3(vec2(1.5 * i_scales.x), 1.5 * i_scales.y) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0); + vec4 eye_position = view_model_matrix * world_position; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; +} diff --git a/resources/shaders/140/imgui.fs b/resources/shaders/140/imgui.fs new file mode 100644 index 0000000000..4b2571749f --- /dev/null +++ b/resources/shaders/140/imgui.fs @@ -0,0 +1,11 @@ +#version 140 + +uniform sampler2D Texture; + +in vec2 Frag_UV; +in vec4 Frag_Color; + +void main() +{ + gl_FragColor = Frag_Color * texture(Texture, Frag_UV.st); +} \ No newline at end of file diff --git a/resources/shaders/140/imgui.vs b/resources/shaders/140/imgui.vs new file mode 100644 index 0000000000..fd743bdf2d --- /dev/null +++ b/resources/shaders/140/imgui.vs @@ -0,0 +1,17 @@ +#version 140 + +uniform mat4 ProjMtx; + +in vec2 Position; +in vec2 UV; +in vec4 Color; + +out vec2 Frag_UV; +out vec4 Frag_Color; + +void main() +{ + Frag_UV = UV; + Frag_Color = Color; + gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/resources/shaders/140/mm_contour.fs b/resources/shaders/140/mm_contour.fs new file mode 100644 index 0000000000..e74124dcae --- /dev/null +++ b/resources/shaders/140/mm_contour.fs @@ -0,0 +1,8 @@ +#version 140 + +uniform vec4 uniform_color; + +void main() +{ + gl_FragColor = uniform_color; +} diff --git a/resources/shaders/140/mm_contour.vs b/resources/shaders/140/mm_contour.vs new file mode 100644 index 0000000000..679291ba6d --- /dev/null +++ b/resources/shaders/140/mm_contour.vs @@ -0,0 +1,15 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform float offset; + +in vec3 v_position; + +void main() +{ + // Add small epsilon to z to solve z-fighting between painted triangles and contour lines. + vec4 clip_position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); + clip_position.z -= offset * abs(clip_position.w); + gl_Position = clip_position; +} diff --git a/resources/shaders/140/mm_gouraud.fs b/resources/shaders/140/mm_gouraud.fs new file mode 100644 index 0000000000..2156394bea --- /dev/null +++ b/resources/shaders/140/mm_gouraud.fs @@ -0,0 +1,90 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float EPSILON = 0.0001; +//BBS: add grey and orange +//const vec3 GREY = vec3(0.9, 0.9, 0.9); +const vec3 ORANGE = vec3(0.8, 0.4, 0.0); +const vec3 LightRed = vec3(0.78, 0.0, 0.0); +const vec3 LightBlue = vec3(0.73, 1.0, 1.0); +uniform vec4 uniform_color; + +uniform bool volume_mirrored; + +uniform mat4 view_model_matrix; +uniform mat3 view_normal_matrix; + +in vec3 clipping_planes_dots; +in vec4 model_pos; +in vec4 world_pos; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; +uniform SlopeDetection slope; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + vec3 color = uniform_color.rgb; + float alpha = uniform_color.a; + + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); +#ifdef FLIP_TRIANGLE_NORMALS + triangle_normal = -triangle_normal; +#endif + + if (volume_mirrored) + triangle_normal = -triangle_normal; + + vec3 transformed_normal = normalize(slope.volume_world_normal_matrix * triangle_normal); + + if (slope.actived) { + if(world_pos.z<0.1&&world_pos.z>-0.1) + { + color = LightBlue; + alpha = 1.0; + } + else if( transformed_normal.z < slope.normal_z - EPSILON) + { + color = color * 0.5 + LightRed * 0.5; + alpha = 1.0; + } + } + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * triangle_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + // x = diffuse, y = specular; + vec2 intensity = vec2(0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (view_model_matrix * model_pos).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha); +} diff --git a/resources/shaders/140/mm_gouraud.vs b/resources/shaders/140/mm_gouraud.vs new file mode 100644 index 0000000000..4add5c0aee --- /dev/null +++ b/resources/shaders/140/mm_gouraud.vs @@ -0,0 +1,35 @@ +#version 140 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +uniform mat4 volume_world_matrix; +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +in vec3 v_position; + +out vec3 clipping_planes_dots; +out vec4 model_pos; +out vec4 world_pos; +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; +uniform SlopeDetection slope; +void main() +{ + model_pos = vec4(v_position, 1.0); + // Point in homogenous coordinates. + world_pos = volume_world_matrix * model_pos; + + gl_Position = projection_matrix * view_model_matrix * model_pos; + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); +} diff --git a/resources/shaders/140/printbed.fs b/resources/shaders/140/printbed.fs new file mode 100644 index 0000000000..6d927a749c --- /dev/null +++ b/resources/shaders/140/printbed.fs @@ -0,0 +1,35 @@ +#version 140 + +const vec3 back_color_dark = vec3(0.235, 0.235, 0.235); +const vec3 back_color_light = vec3(0.365, 0.365, 0.365); + +uniform sampler2D in_texture; +uniform bool transparent_background; +uniform bool svg_source; + +in vec2 tex_coord; +out vec4 frag_color; + +vec4 svg_color() +{ + // takes foreground from texture + vec4 fore_color = texture(in_texture, tex_coord); + + // calculates radial gradient + vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coord.xy) - vec2(0.5))))); + + // blends foreground with background + return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); +} + +vec4 non_svg_color() +{ + // takes foreground from texture + vec4 color = texture(in_texture, tex_coord); + return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a); +} + +void main() +{ + frag_color = svg_source ? svg_color() : non_svg_color(); +} \ No newline at end of file diff --git a/resources/shaders/140/printbed.vs b/resources/shaders/140/printbed.vs new file mode 100644 index 0000000000..57d8ca3b7c --- /dev/null +++ b/resources/shaders/140/printbed.vs @@ -0,0 +1,15 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +in vec3 v_position; +in vec2 v_tex_coord; + +out vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/thumbnail.fs b/resources/shaders/140/thumbnail.fs new file mode 100644 index 0000000000..9e6d5d854c --- /dev/null +++ b/resources/shaders/140/thumbnail.fs @@ -0,0 +1,16 @@ +#version 140 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +in vec2 intensity; +//varying float drop; +in vec4 world_pos; + +void main() +{ + if (world_pos.z < 0.0) + discard; + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/140/thumbnail.vs b/resources/shaders/140/thumbnail.vs new file mode 100644 index 0000000000..90ca720a37 --- /dev/null +++ b/resources/shaders/140/thumbnail.vs @@ -0,0 +1,50 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +in vec3 v_position; +in vec3 v_normal; + +// x = tainted, y = specular; +out vec2 intensity; +out vec4 world_pos; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + world_pos = volume_world_matrix * vec4(v_position, 1.0); + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/140/variable_layer_height.fs b/resources/shaders/140/variable_layer_height.fs new file mode 100644 index 0000000000..cf1fc309cc --- /dev/null +++ b/resources/shaders/140/variable_layer_height.fs @@ -0,0 +1,41 @@ +#version 140 + +#define M_PI 3.1415926535897932384626433832795 + +// 2D texture (1D texture split by the rows) of color along the object Z axis. +uniform sampler2D z_texture; +// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. +uniform float z_to_texture_row; +uniform float z_texture_row_to_normalized; +uniform float z_cursor; +uniform float z_cursor_band_width; + +// x = tainted, y = specular; +in vec2 intensity; + +in float object_z; + +void main() +{ + float object_z_row = z_to_texture_row * object_z; + // Index of the row in the texture. + float z_texture_row = floor(object_z_row); + // Normalized coordinate from 0. to 1. + float z_texture_col = object_z_row - z_texture_row; + float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; + // Calculate level of detail from the object Z coordinate. + // This makes the slowly sloping surfaces to be shown with high detail (with stripes), + // and the vertical surfaces to be shown with low detail (no stripes) + float z_in_cells = object_z_row * 190.; + // Gradient of Z projected on the screen. + float dx_vtc = dFdx(z_in_cells); + float dy_vtc = dFdy(z_in_cells); + float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.); + // Sample the Z texture. Texture coordinates are normalized to <0, 1>. + vec4 color = vec4(0.25, 0.25, 0.25, 1.0); + if (z_texture_row >= 0.0) + color = mix(texture(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), + texture(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); + // Mix the final color. + gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); +} diff --git a/resources/shaders/140/variable_layer_height.vs b/resources/shaders/140/variable_layer_height.vs new file mode 100644 index 0000000000..d8deb2f9ee --- /dev/null +++ b/resources/shaders/140/variable_layer_height.vs @@ -0,0 +1,60 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform float object_max_z; + +in vec3 v_position; +in vec3 v_normal; +in vec2 v_tex_coord; + +// x = tainted, y = specular; +out vec2 intensity; + +out float object_z; + +void main() +{ + // ===================================================== + // NOTE: + // when object_max_z > 0.0 we are rendering the overlay + // when object_max_z == 0.0 we are rendering the volumes + // ===================================================== + + // First transform the normal into camera space and normalize the result. + vec3 normal = (object_max_z > 0.0) ? vec3(0.0, 0.0, 1.0) : normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular) + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Scaled to widths of the Z texture. + object_z = (object_max_z > 0.0) ? object_max_z * v_tex_coord.y : (volume_world_matrix * vec4(v_position, 1.0)).z; + + gl_Position = projection_matrix * position; +} diff --git a/resources/shaders/background.fs b/resources/shaders/background.fs new file mode 100644 index 0000000000..b148440898 --- /dev/null +++ b/resources/shaders/background.fs @@ -0,0 +1,11 @@ +#version 110 + +uniform vec4 top_color; +uniform vec4 bottom_color; + +varying vec2 tex_coord; + +void main() +{ + gl_FragColor = mix(bottom_color, top_color, tex_coord.y); +} diff --git a/resources/shaders/background.vs b/resources/shaders/background.vs new file mode 100644 index 0000000000..b7c1d92c0e --- /dev/null +++ b/resources/shaders/background.vs @@ -0,0 +1,9 @@ +#version 110 + +varying vec2 tex_coord; + +void main() +{ + gl_Position = gl_Vertex; + tex_coord = gl_MultiTexCoord0.xy; +} diff --git a/resources/shaders/flat.fs b/resources/shaders/flat.fs new file mode 100644 index 0000000000..ab656998df --- /dev/null +++ b/resources/shaders/flat.fs @@ -0,0 +1,8 @@ +#version 110 + +uniform vec4 uniform_color; + +void main() +{ + gl_FragColor = uniform_color; +} diff --git a/resources/shaders/cali.vs b/resources/shaders/flat.vs similarity index 100% rename from resources/shaders/cali.vs rename to resources/shaders/flat.vs diff --git a/resources/shaders/flat_texture.fs b/resources/shaders/flat_texture.fs new file mode 100644 index 0000000000..ffe193b1c0 --- /dev/null +++ b/resources/shaders/flat_texture.fs @@ -0,0 +1,10 @@ +#version 110 + +uniform sampler2D uniform_texture; + +varying vec2 tex_coord; + +void main() +{ + gl_FragColor = texture2D(uniform_texture, tex_coord); +} diff --git a/resources/shaders/flat_texture.vs b/resources/shaders/flat_texture.vs new file mode 100644 index 0000000000..27addc7526 --- /dev/null +++ b/resources/shaders/flat_texture.vs @@ -0,0 +1,9 @@ +#version 110 + +varying vec2 tex_coord; + +void main() +{ + gl_Position = ftransform(); + tex_coord = gl_MultiTexCoord0.xy; +} diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs index 3f12a6e92c..4084efdbf1 100644 --- a/resources/shaders/gouraud.fs +++ b/resources/shaders/gouraud.fs @@ -48,7 +48,6 @@ varying vec2 intensity; uniform PrintVolumeDetection print_volume; -varying vec4 model_pos; varying vec4 world_pos; varying float world_normal_z; varying vec3 eye_normal; diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index 79d7a63c07..c8b3d7b335 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -38,7 +38,6 @@ varying vec2 intensity; varying vec3 clipping_planes_dots; -varying vec4 model_pos; varying vec4 world_pos; varying float world_normal_z; varying vec3 eye_normal; @@ -60,7 +59,6 @@ void main() NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - model_pos = gl_Vertex; // Point in homogenous coordinates. world_pos = volume_world_matrix * gl_Vertex; diff --git a/resources/shaders/options_110.vs b/resources/shaders/options_110.vs deleted file mode 100644 index 5f2ab23504..0000000000 --- a/resources/shaders/options_110.vs +++ /dev/null @@ -1,22 +0,0 @@ -#version 110 - -uniform bool use_fixed_screen_size; -uniform float zoom; -uniform float point_size; -uniform float near_plane_height; - -float fixed_screen_size() -{ - return point_size; -} - -float fixed_world_size() -{ - return (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; -} - -void main() -{ - gl_Position = ftransform(); - gl_PointSize = use_fixed_screen_size ? fixed_screen_size() : fixed_world_size(); -} diff --git a/resources/shaders/options_120.fs b/resources/shaders/options_120.fs deleted file mode 100644 index e9b61304f2..0000000000 --- a/resources/shaders/options_120.fs +++ /dev/null @@ -1,22 +0,0 @@ -// version 120 is needed for gl_PointCoord -#version 120 - -uniform vec4 uniform_color; -uniform float percent_outline_radius; -uniform float percent_center_radius; - -vec4 calc_color(float radius, vec4 color) -{ - return ((radius < percent_center_radius) || (radius > 1.0 - percent_outline_radius)) ? - vec4(0.5 * color.rgb, color.a) : color; -} - -void main() -{ - vec2 pos = (gl_PointCoord - 0.5) * 2.0; - float radius = length(pos); - if (radius > 1.0) - discard; - - gl_FragColor = calc_color(radius, uniform_color); -} diff --git a/resources/shaders/options_120.vs b/resources/shaders/options_120.vs deleted file mode 100644 index edb503fb2b..0000000000 --- a/resources/shaders/options_120.vs +++ /dev/null @@ -1,22 +0,0 @@ -#version 120 - -uniform bool use_fixed_screen_size; -uniform float zoom; -uniform float point_size; -uniform float near_plane_height; - -float fixed_screen_size() -{ - return point_size; -} - -float fixed_world_size() -{ - return (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w; -} - -void main() -{ - gl_Position = ftransform(); - gl_PointSize = use_fixed_screen_size ? fixed_screen_size() : fixed_world_size(); -} diff --git a/resources/shaders/printbed.fs b/resources/shaders/printbed.fs index d1316ca2fe..833dff08f4 100644 --- a/resources/shaders/printbed.fs +++ b/resources/shaders/printbed.fs @@ -1,21 +1,21 @@ #version 110 -const vec3 back_color_dark = vec3(0.235, 0.235, 0.235); +const vec3 back_color_dark = vec3(0.235, 0.235, 0.235); const vec3 back_color_light = vec3(0.365, 0.365, 0.365); uniform sampler2D texture; uniform bool transparent_background; uniform bool svg_source; -varying vec2 tex_coords; +varying vec2 tex_coord; vec4 svg_color() { // takes foreground from texture - vec4 fore_color = texture2D(texture, tex_coords); + vec4 fore_color = texture2D(texture, tex_coord); // calculates radial gradient - vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5))))); + vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coord.xy) - vec2(0.5))))); // blends foreground with background return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); @@ -24,7 +24,7 @@ vec4 svg_color() vec4 non_svg_color() { // takes foreground from texture - vec4 color = texture2D(texture, tex_coords); + vec4 color = texture2D(texture, tex_coord); return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a); } diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs index 7633017f12..27addc7526 100644 --- a/resources/shaders/printbed.vs +++ b/resources/shaders/printbed.vs @@ -1,14 +1,9 @@ #version 110 -attribute vec3 v_position; -attribute vec2 v_tex_coords; - -varying vec2 tex_coords; +varying vec2 tex_coord; void main() { - gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0); - // the following line leads to crash on some Intel graphics card - //gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0); - tex_coords = v_tex_coords; + gl_Position = ftransform(); + tex_coord = gl_MultiTexCoord0.xy; } diff --git a/src/BaseException.cpp b/src/BaseException.cpp index d51a750577..2443ebe4bb 100644 --- a/src/BaseException.cpp +++ b/src/BaseException.cpp @@ -91,8 +91,26 @@ void CBaseException::ShowLoadModules() void CBaseException::ShowCallstack(HANDLE hThread, const CONTEXT* context) { - OutputString(_T("Show CallStack:\r\n")); - LPSTACKINFO phead = StackWalker(hThread, context); + OutputString(_T("Show CallStack:\n")); + LPSTACKINFO phead = StackWalker(hThread, context); + + // Show RVA of each call stack, so we can locate the symbol using pdb file + // To show the symbol, load the in WinDBG with pdb file, then type the following commands: + // > lm which gives you the start address of each module, as well as module names + // > !dh list all module headers. Find the of the section given by + // the
output in the crash log + // > ln +
+ reveals the debug symbol + OutputString(_T("\nLogical Address:\n")); + TCHAR szFaultingModule[MAX_PATH]; + DWORD section, offset; + for (LPSTACKINFO ps = phead; ps != nullptr; ps = ps->pNext) { + if (GetLogicalAddress((PVOID) ps->szFncAddr, szFaultingModule, sizeof(szFaultingModule), section, offset)) { + OutputString(_T("0x%X 0x%X:0x%X %s\n"), ps->szFncAddr, section, offset, szFaultingModule); + } else { + OutputString(_T("0x%X Unknown\n"), ps->szFncAddr); + } + } + FreeStackInformations(phead); } diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 3ab0342d2c..7271af2d97 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -1553,23 +1553,11 @@ int CLI::run(int argc, char **argv) o->cut(Z, m_config.opt_float("cut"), &out); } #else - ModelObject* object = model.objects.front(); - const BoundingBoxf3& box = object->bounding_box(); - const float Margin = 20.0; - const float max_x = box.size()(0) / 2.0 + Margin; - const float min_x = -max_x; - const float max_y = box.size()(1) / 2.0 + Margin; - const float min_y = -max_y; - - std::array plane_points; - plane_points[0] = { min_x, min_y, 0 }; - plane_points[1] = { max_x, min_y, 0 }; - plane_points[2] = { max_x, max_y, 0 }; - plane_points[3] = { min_x, max_y, 0 }; - for (Vec3d& point : plane_points) { - point += box.center(); - } - model.objects.front()->cut(0, plane_points, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower); + Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), + ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper); + auto cut_objects = cut.perform_with_plane(); + for (ModelObject* obj : cut_objects) + model.add_object(*obj); #endif model.delete_object(size_t(0)); } @@ -2279,12 +2267,12 @@ int CLI::run(int argc, char **argv) else colors.push_back("#FFFFFF"); - std::vector> colors_out(colors.size()); - unsigned char rgb_color[3] = {}; + std::vector colors_out(colors.size()); + ColorRGBA rgb_color; for (const std::string& color : colors) { - Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); + Slic3r::decode_color(color, rgb_color); size_t color_idx = &color - &colors.front(); - colors_out[color_idx] = { float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f }; + colors_out[color_idx] = rgb_color; } int gl_major, gl_minor, gl_verbos; @@ -2353,19 +2341,16 @@ int CLI::run(int argc, char **argv) // continue; for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { const ModelInstance &model_instance = *model_object.instances[instance_idx]; - glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true); + glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, false, true); //glvolume_collection.volumes.back()->geometry_id = key.geometry_id; std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00"; - unsigned char rgb_color[3] = {}; - Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); - glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f); + ColorRGBA rgb_color; + Slic3r::decode_color(color, rgb_color); + glvolume_collection.volumes.back()->set_render_color(rgb_color); - std::array new_color; - new_color[0] = float(rgb_color[0]) / 255.f; - new_color[1] = float(rgb_color[1]) / 255.f; - new_color[2] = float(rgb_color[2]) / 255.f; - new_color[3] = 1.f; + ColorRGBA new_color; + new_color = rgb_color; glvolume_collection.volumes.back()->set_color(new_color); glvolume_collection.volumes.back()->printable = model_instance.printable; } diff --git a/src/StackWalker.cpp b/src/StackWalker.cpp index 2d20cbd0da..818ac3405e 100644 --- a/src/StackWalker.cpp +++ b/src/StackWalker.cpp @@ -491,7 +491,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context) }else { //调用错误一般是487(地址无效或者没有访问的权限、在符号表中未找到指定地址的相关信息) - this->OutputString(_T("Call SymGetSymFromAddr64 ,Address %08x Error:%08x\r\n"), sf.AddrPC.Offset, GetLastError()); + //this->OutputString(_T("Call SymGetSymFromAddr64 ,Address %08x Error:%08x\n"), sf.AddrPC.Offset, GetLastError()); StringCchCopy(pCallStack->undFullName, STACKWALK_MAX_NAMELEN, textconv_helper::A2T_("Unknown")); } @@ -502,14 +502,14 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context) pCallStack->uFileNum = pLine->LineNumber; }else { - this->OutputString(_T("Call SymGetLineFromAddr64 ,Address %08x Error:%08x\r\n"), sf.AddrPC.Offset, GetLastError()); + //this->OutputString(_T("Call SymGetLineFromAddr64 ,Address %08x Error:%08x\n"), sf.AddrPC.Offset, GetLastError()); StringCchCopy(pCallStack->szFileName, MAX_PATH, textconv_helper::A2T_("Unknown file")); pCallStack->uFileNum = -1; } //这里为了将获取函数信息失败的情况与正常的情况一起输出,防止用户在查看时出现误解 - this->OutputString(_T("%08llx:%s [%s][%ld]\r\n"), pCallStack->szFncAddr, pCallStack->undFullName, pCallStack->szFileName, pCallStack->uFileNum); + this->OutputString(_T("%08llx:%s [%s][%ld]\n"), pCallStack->szFncAddr, pCallStack->undFullName, pCallStack->szFileName, pCallStack->uFileNum); if (NULL == pHead) { pHead = pCallStack; diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 1774eb28ac..9380ea1649 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -161,6 +161,7 @@ namespace ImGui const wchar_t ClippyMarker = 0x0802; const wchar_t InfoMarker = 0x0803; const wchar_t SliderFloatEditBtnIcon = 0x0804; + const wchar_t ClipboardBtnIcon = 0x0805; // BBS const wchar_t CircleButtonIcon = 0x0810; @@ -196,6 +197,7 @@ namespace ImGui const wchar_t CloseBlockNotifButton = 0x0833; const wchar_t CloseBlockNotifHoverButton = 0x0834; const wchar_t BlockNotifErrorIcon = 0x0835; + const wchar_t ClipboardBtnDarkIcon = 0x0836; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/imgui/imgui.cpp b/src/imgui/imgui.cpp index fd32e4cf5f..8f45263a39 100644 --- a/src/imgui/imgui.cpp +++ b/src/imgui/imgui.cpp @@ -6046,8 +6046,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = FindBestWindowPosForPopup(window); + // Orca: Allow fixed tooltip pos while still being clamped inside the render area + else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_is_child_tooltip) { + if (window_pos_set_by_api) { + // Hack: add ImGuiWindowFlags_Popup so it does not follow cursor + ImGuiWindowFlags old_flags = window->Flags; + window->Flags |= ImGuiWindowFlags_Popup; + window->Pos = FindBestWindowPosForPopup(window); + window->Flags = old_flags; + } else { + window->Pos = FindBestWindowPosForPopup(window); + } + } // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. diff --git a/src/libslic3r/BuildVolume.hpp b/src/libslic3r/BuildVolume.hpp index 4ab007f8b8..0df41f1be5 100644 --- a/src/libslic3r/BuildVolume.hpp +++ b/src/libslic3r/BuildVolume.hpp @@ -93,6 +93,9 @@ public: // Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices. bool all_paths_inside_vertices_and_normals_interleaved(const std::vector& paths, const Eigen::AlignedBox& bbox, bool ignore_bottom = true) const; + const std::pair, std::vector>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; } + const std::pair, std::vector>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; } + private: // Source definition of the print bed geometry (PrintConfig::printable_area) std::vector m_bed_shape; diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3cfb82b073..0134707178 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -56,6 +56,8 @@ set(lisbslic3r_sources Clipper2Utils.cpp Clipper2Utils.hpp ClipperZUtils.hpp + Color.cpp + Color.hpp Config.cpp Config.hpp CurveAnalyzer.cpp @@ -200,12 +202,17 @@ set(lisbslic3r_sources BlacklistedLibraryCheck.hpp LocalesUtils.cpp LocalesUtils.hpp + CutUtils.cpp + CutUtils.hpp Model.cpp Model.hpp ModelArrange.hpp ModelArrange.cpp MultiMaterialSegmentation.cpp MultiMaterialSegmentation.hpp + Measure.hpp + Measure.cpp + MeasureUtils.hpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp @@ -305,6 +312,7 @@ set(lisbslic3r_sources Surface.hpp SurfaceCollection.cpp SurfaceCollection.hpp + SurfaceMesh.hpp SVG.cpp SVG.hpp Technologies.hpp diff --git a/src/libslic3r/Color.cpp b/src/libslic3r/Color.cpp new file mode 100644 index 0000000000..c282e307ed --- /dev/null +++ b/src/libslic3r/Color.cpp @@ -0,0 +1,420 @@ +#include "libslic3r.h" +#include "Color.hpp" + +#include + +static const float INV_255 = 1.0f / 255.0f; + +namespace Slic3r { + +// Conversion from RGB to HSV color space +// The input RGB values are in the range [0, 1] +// The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1] +static void RGBtoHSV(float r, float g, float b, float& h, float& s, float& v) +{ + assert(0.0f <= r && r <= 1.0f); + assert(0.0f <= g && g <= 1.0f); + assert(0.0f <= b && b <= 1.0f); + + const float max_comp = std::max(std::max(r, g), b); + const float min_comp = std::min(std::min(r, g), b); + const float delta = max_comp - min_comp; + + if (delta > 0.0f) { + if (max_comp == r) + h = 60.0f * (std::fmod(((g - b) / delta), 6.0f)); + else if (max_comp == g) + h = 60.0f * (((b - r) / delta) + 2.0f); + else if (max_comp == b) + h = 60.0f * (((r - g) / delta) + 4.0f); + + s = (max_comp > 0.0f) ? delta / max_comp : 0.0f; + } + else { + h = 0.0f; + s = 0.0f; + } + v = max_comp; + + while (h < 0.0f) { h += 360.0f; } + while (h > 360.0f) { h -= 360.0f; } + + assert(0.0f <= s && s <= 1.0f); + assert(0.0f <= v && v <= 1.0f); + assert(0.0f <= h && h <= 360.0f); +} + +// Conversion from HSV to RGB color space +// The input HSV values are in the ranges h = [0, 360], and s, v = [0, 1] +// The output RGB values are in the range [0, 1] +static void HSVtoRGB(float h, float s, float v, float& r, float& g, float& b) +{ + assert(0.0f <= s && s <= 1.0f); + assert(0.0f <= v && v <= 1.0f); + assert(0.0f <= h && h <= 360.0f); + + const float chroma = v * s; + const float h_prime = std::fmod(h / 60.0f, 6.0f); + const float x = chroma * (1.0f - std::abs(std::fmod(h_prime, 2.0f) - 1.0f)); + const float m = v - chroma; + + if (0.0f <= h_prime && h_prime < 1.0f) { + r = chroma; + g = x; + b = 0.0f; + } + else if (1.0f <= h_prime && h_prime < 2.0f) { + r = x; + g = chroma; + b = 0.0f; + } + else if (2.0f <= h_prime && h_prime < 3.0f) { + r = 0.0f; + g = chroma; + b = x; + } + else if (3.0f <= h_prime && h_prime < 4.0f) { + r = 0.0f; + g = x; + b = chroma; + } + else if (4.0f <= h_prime && h_prime < 5.0f) { + r = x; + g = 0.0f; + b = chroma; + } + else if (5.0f <= h_prime && h_prime < 6.0f) { + r = chroma; + g = 0.0f; + b = x; + } + else { + r = 0.0f; + g = 0.0f; + b = 0.0f; + } + + r += m; + g += m; + b += m; + + assert(0.0f <= r && r <= 1.0f); + assert(0.0f <= g && g <= 1.0f); + assert(0.0f <= b && b <= 1.0f); +} + +class Randomizer +{ + std::random_device m_rd; + +public: + float random_float(float min, float max) { + std::mt19937 rand_generator(m_rd()); + std::uniform_real_distribution distrib(min, max); + return distrib(rand_generator); + } +}; + +ColorRGB::ColorRGB(float r, float g, float b) +: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) }) +{ +} + +ColorRGB::ColorRGB(unsigned char r, unsigned char g, unsigned char b) +: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f) }) +{ +} + +bool ColorRGB::operator < (const ColorRGB& other) const +{ + for (size_t i = 0; i < 3; ++i) { + if (m_data[i] < other.m_data[i]) + return true; + else if (m_data[i] > other.m_data[i]) + return false; + } + + return false; +} + +bool ColorRGB::operator > (const ColorRGB& other) const +{ + for (size_t i = 0; i < 3; ++i) { + if (m_data[i] > other.m_data[i]) + return true; + else if (m_data[i] < other.m_data[i]) + return false; + } + + return false; +} + +ColorRGB ColorRGB::operator + (const ColorRGB& other) const +{ + ColorRGB ret; + for (size_t i = 0; i < 3; ++i) { + ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f); + } + return ret; +} + +ColorRGB ColorRGB::operator * (float value) const +{ + assert(value >= 0.0f); + ColorRGB ret; + for (size_t i = 0; i < 3; ++i) { + ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f); + } + return ret; +} + +ColorRGBA::ColorRGBA(float r, float g, float b, float a) +: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f), std::clamp(a, 0.0f, 1.0f) }) +{ +} + +ColorRGBA::ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f), std::clamp(a * INV_255, 0.0f, 1.0f) }) +{ +} + +bool ColorRGBA::operator < (const ColorRGBA& other) const +{ + for (size_t i = 0; i < 3; ++i) { + if (m_data[i] < other.m_data[i]) + return true; + else if (m_data[i] > other.m_data[i]) + return false; + } + + return false; +} + +bool ColorRGBA::operator > (const ColorRGBA& other) const +{ + for (size_t i = 0; i < 3; ++i) { + if (m_data[i] > other.m_data[i]) + return true; + else if (m_data[i] < other.m_data[i]) + return false; + } + + return false; +} + +ColorRGBA ColorRGBA::operator + (const ColorRGBA& other) const +{ + ColorRGBA ret; + for (size_t i = 0; i < 3; ++i) { + ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f); + } + return ret; +} + +ColorRGBA ColorRGBA::operator * (float value) const +{ + assert(value >= 0.0f); + ColorRGBA ret; + for (size_t i = 0; i < 3; ++i) { + ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f); + } + ret.m_data[3] = this->m_data[3]; + return ret; +} + +ColorRGB operator * (float value, const ColorRGB& other) { return other * value; } +ColorRGBA operator * (float value, const ColorRGBA& other) { return other * value; } + +ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t) +{ + assert(0.0f <= t && t <= 1.0f); + return (1.0f - t) * a + t * b; +} + +ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t) +{ + assert(0.0f <= t && t <= 1.0f); + return (1.0f - t) * a + t * b; +} + +ColorRGB complementary(const ColorRGB& color) +{ + return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b() }; +} + +ColorRGBA complementary(const ColorRGBA& color) +{ + return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b(), color.a() }; +} + +ColorRGB saturate(const ColorRGB& color, float factor) +{ + float h, s, v; + RGBtoHSV(color.r(), color.g(), color.b(), h, s, v); + s = std::clamp(s * factor, 0.0f, 1.0f); + float r, g, b; + HSVtoRGB(h, s, v, r, g, b); + return { r, g, b }; +} + +ColorRGBA saturate(const ColorRGBA& color, float factor) +{ + return to_rgba(saturate(to_rgb(color), factor), color.a()); +} + +ColorRGB opposite(const ColorRGB& color) +{ + float h, s, v; + RGBtoHSV(color.r(), color.g(), color.b(), h, s, v); + + h += 65.0f; // 65 instead 60 to avoid circle values + if (h > 360.0f) + h -= 360.0f; + + Randomizer rnd; + s = rnd.random_float(0.65f, 1.0f); + v = rnd.random_float(0.65f, 1.0f); + + float r, g, b; + HSVtoRGB(h, s, v, r, g, b); + return { r, g, b }; +} + +ColorRGB opposite(const ColorRGB& a, const ColorRGB& b) +{ + float ha, sa, va; + RGBtoHSV(a.r(), a.g(), a.b(), ha, sa, va); + float hb, sb, vb; + RGBtoHSV(b.r(), b.g(), b.b(), hb, sb, vb); + + float delta_h = std::abs(ha - hb); + float start_h = (delta_h > 180.0f) ? std::min(ha, hb) : std::max(ha, hb); + + start_h += 5.0f; // to avoid circle change of colors for 120 deg + if (delta_h < 180.0f) + delta_h = 360.0f - delta_h; + + Randomizer rnd; + float out_h = start_h + 0.5f * delta_h; + if (out_h > 360.0f) + out_h -= 360.0f; + float out_s = rnd.random_float(0.65f, 1.0f); + float out_v = rnd.random_float(0.65f, 1.0f); + + float out_r, out_g, out_b; + HSVtoRGB(out_h, out_s, out_v, out_r, out_g, out_b); + return { out_r, out_g, out_b }; +} + +bool can_decode_color(const std::string &color) +{ + return (color.size() == 7 && color.front() == '#') || (color.size() == 9 && color.front() == '#'); +} + +bool decode_color(const std::string& color_in, ColorRGB& color_out) +{ + ColorRGBA rgba; + if (!decode_color(color_in, rgba)) + return false; + + color_out = to_rgb(rgba); + return true; +} + +bool decode_color(const std::string& color_in, ColorRGBA& color_out) +{ + auto hex_digit_to_int = [](const char c) { + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; + }; + + color_out = ColorRGBA::BLACK(); + if (can_decode_color(color_in)) { + const char *c = color_in.data() + 1; + if (color_in.size() == 7) { + for (unsigned int i = 0; i < 3; ++i) { + const int digit1 = hex_digit_to_int(*c++); + const int digit2 = hex_digit_to_int(*c++); + if (digit1 != -1 && digit2 != -1) + color_out.set(i, float(digit1 * 16 + digit2) * INV_255); + } + } else { + for (unsigned int i = 0; i < 4; ++i) { + const int digit1 = hex_digit_to_int(*c++); + const int digit2 = hex_digit_to_int(*c++); + if (digit1 != -1 && digit2 != -1) + color_out.set(i, float(digit1 * 16 + digit2) * INV_255); + } + } + } else + return false; + + assert(0.0f <= color_out.r() && color_out.r() <= 1.0f); + assert(0.0f <= color_out.g() && color_out.g() <= 1.0f); + assert(0.0f <= color_out.b() && color_out.b() <= 1.0f); + assert(0.0f <= color_out.a() && color_out.a() <= 1.0f); + return true; +} + +bool decode_colors(const std::vector& colors_in, std::vector& colors_out) +{ + colors_out = std::vector(colors_in.size(), ColorRGB::BLACK()); + for (size_t i = 0; i < colors_in.size(); ++i) { + if (!decode_color(colors_in[i], colors_out[i])) + return false; + } + return true; +} + +bool decode_colors(const std::vector& colors_in, std::vector& colors_out) +{ + colors_out = std::vector(colors_in.size(), ColorRGBA::BLACK()); + for (size_t i = 0; i < colors_in.size(); ++i) { + if (!decode_color(colors_in[i], colors_out[i])) + return false; + } + return true; +} + +std::string encode_color(const ColorRGB& color) +{ + char buffer[64]; + ::sprintf(buffer, "#%02X%02X%02X", color.r_uchar(), color.g_uchar(), color.b_uchar()); + return std::string(buffer); +} + +std::string encode_color(const ColorRGBA& color) { return encode_color(to_rgb(color)); } + +ColorRGB to_rgb(const ColorRGBA& other_rgba) { return { other_rgba.r(), other_rgba.g(), other_rgba.b() }; } +ColorRGBA to_rgba(const ColorRGB& other_rgb) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), 1.0f }; } +ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), alpha }; } + +ColorRGBA picking_decode(unsigned int id) +{ + return { + float((id >> 0) & 0xff) * INV_255, // red + float((id >> 8) & 0xff) * INV_255, // green + float((id >> 16) & 0xff) * INV_255, // blue + float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff)) * INV_255 // checksum for validating against unwanted alpha blending and multi sampling + }; +} + +unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b) { return r + (g << 8) + (b << 16); } + +unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue) +{ + // 8 bit hash for the color + unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff; + // Increase enthropy by a bit reversal + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + // Flip every second bit to increase the enthropy even more. + b ^= 0x55; + return b; +} + +} // namespace Slic3r + diff --git a/src/libslic3r/Color.hpp b/src/libslic3r/Color.hpp new file mode 100644 index 0000000000..ea17328bec --- /dev/null +++ b/src/libslic3r/Color.hpp @@ -0,0 +1,175 @@ +#ifndef slic3r_Color_hpp_ +#define slic3r_Color_hpp_ + +#include +#include + +namespace Slic3r { + +class ColorRGB +{ + std::array m_data{1.0f, 1.0f, 1.0f}; + +public: + ColorRGB() = default; + ColorRGB(float r, float g, float b); + ColorRGB(unsigned char r, unsigned char g, unsigned char b); + ColorRGB(const ColorRGB& other) = default; + + ColorRGB& operator = (const ColorRGB& other) { m_data = other.m_data; return *this; } + + bool operator == (const ColorRGB& other) const { return m_data == other.m_data; } + bool operator != (const ColorRGB& other) const { return !operator==(other); } + bool operator < (const ColorRGB& other) const; + bool operator > (const ColorRGB& other) const; + + ColorRGB operator + (const ColorRGB& other) const; + ColorRGB operator * (float value) const; + + const float* const data() const { return m_data.data(); } + + float r() const { return m_data[0]; } + float g() const { return m_data[1]; } + float b() const { return m_data[2]; } + + void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); } + void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); } + void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); } + + void set(unsigned int comp, float value) { + assert(0 <= comp && comp <= 2); + m_data[comp] = std::clamp(value, 0.0f, 1.0f); + } + + unsigned char r_uchar() const { return static_cast(m_data[0] * 255.0f); } + unsigned char g_uchar() const { return static_cast(m_data[1] * 255.0f); } + unsigned char b_uchar() const { return static_cast(m_data[2] * 255.0f); } + + static const ColorRGB BLACK() { return { 0.0f, 0.0f, 0.0f }; } + static const ColorRGB BLUE() { return { 0.0f, 0.0f, 1.0f }; } + static const ColorRGB BLUEISH() { return { 0.5f, 0.5f, 1.0f }; } + static const ColorRGB CYAN() { return { 0.0f, 1.0f, 1.0f }; } + static const ColorRGB DARK_GRAY() { return { 0.25f, 0.25f, 0.25f }; } + static const ColorRGB DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f }; } + static const ColorRGB GRAY() { return { 0.5f, 0.5f, 0.5f }; } + static const ColorRGB GREEN() { return { 0.0f, 1.0f, 0.0f }; } + static const ColorRGB GREENISH() { return { 0.5f, 1.0f, 0.5f }; } + static const ColorRGB LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f }; } + static const ColorRGB MAGENTA() { return { 1.0f, 0.0f, 1.0f }; } + static const ColorRGB ORANGE() { return { 0.92f, 0.50f, 0.26f }; } + static const ColorRGB RED() { return { 1.0f, 0.0f, 0.0f }; } + static const ColorRGB REDISH() { return { 1.0f, 0.5f, 0.5f }; } + static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; } + static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; } + + static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; } + static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; } + static const ColorRGB Z() { return { 0.0f, 0.0f, 0.75f }; } +}; + +class ColorRGBA +{ + std::array m_data{ 1.0f, 1.0f, 1.0f, 1.0f }; + +public: + ColorRGBA() = default; + ColorRGBA(float r, float g, float b, float a); + ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); + ColorRGBA(const ColorRGBA& other) = default; + + ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; } + + bool operator == (const ColorRGBA& other) const { return m_data == other.m_data; } + bool operator != (const ColorRGBA& other) const { return !operator==(other); } + bool operator < (const ColorRGBA& other) const; + bool operator > (const ColorRGBA& other) const; + + ColorRGBA operator + (const ColorRGBA& other) const; + ColorRGBA operator * (float value) const; + + const float* const data() const { return m_data.data(); } + + float r() const { return m_data[0]; } + float g() const { return m_data[1]; } + float b() const { return m_data[2]; } + float a() const { return m_data[3]; } + + void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); } + void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); } + void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); } + void a(float a) { m_data[3] = std::clamp(a, 0.0f, 1.0f); } + + void set(unsigned int comp, float value) { + assert(0 <= comp && comp <= 3); + m_data[comp] = std::clamp(value, 0.0f, 1.0f); + } + + unsigned char r_uchar() const { return static_cast(m_data[0] * 255.0f); } + unsigned char g_uchar() const { return static_cast(m_data[1] * 255.0f); } + unsigned char b_uchar() const { return static_cast(m_data[2] * 255.0f); } + unsigned char a_uchar() const { return static_cast(m_data[3] * 255.0f); } + + bool is_transparent() const { return m_data[3] < 1.0f; } + + static const ColorRGBA BLACK() { return { 0.0f, 0.0f, 0.0f, 1.0f }; } + static const ColorRGBA BLUE() { return { 0.0f, 0.0f, 1.0f, 1.0f }; } + static const ColorRGBA BLUEISH() { return { 0.5f, 0.5f, 1.0f, 1.0f }; } + static const ColorRGBA CYAN() { return { 0.0f, 1.0f, 1.0f, 1.0f }; } + static const ColorRGBA DARK_GRAY() { return { 0.25f, 0.25f, 0.25f, 1.0f }; } + static const ColorRGBA DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f, 1.0f }; } + static const ColorRGBA GRAY() { return { 0.5f, 0.5f, 0.5f, 1.0f }; } + static const ColorRGBA GREEN() { return { 0.0f, 1.0f, 0.0f, 1.0f }; } + static const ColorRGBA GREENISH() { return { 0.5f, 1.0f, 0.5f, 1.0f }; } + static const ColorRGBA LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f, 1.0f }; } + static const ColorRGBA MAGENTA() { return { 1.0f, 0.0f, 1.0f, 1.0f }; } + static const ColorRGBA ORANGE() { return { 0.923f, 0.504f, 0.264f, 1.0f }; } + static const ColorRGBA RED() { return { 1.0f, 0.0f, 0.0f, 1.0f }; } + static const ColorRGBA REDISH() { return { 1.0f, 0.5f, 0.5f, 1.0f }; } + static const ColorRGBA YELLOW() { return { 1.0f, 1.0f, 0.0f, 1.0f }; } + static const ColorRGBA WHITE() { return { 1.0f, 1.0f, 1.0f, 1.0f }; } + static const ColorRGBA ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255, 1.0f}; } + + static const ColorRGBA X() { return { 0.75f, 0.0f, 0.0f, 1.0f }; } + static const ColorRGBA Y() { return { 0.0f, 0.75f, 0.0f, 1.0f }; } + static const ColorRGBA Z() { return { 0.0f, 0.0f, 0.75f, 1.0f }; } +}; + +ColorRGB operator * (float value, const ColorRGB& other); +ColorRGBA operator * (float value, const ColorRGBA& other); + +ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t); +ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t); + +ColorRGB complementary(const ColorRGB& color); +ColorRGBA complementary(const ColorRGBA& color); + +ColorRGB saturate(const ColorRGB& color, float factor); +ColorRGBA saturate(const ColorRGBA& color, float factor); + +ColorRGB opposite(const ColorRGB& color); +ColorRGB opposite(const ColorRGB& a, const ColorRGB& b); + +bool can_decode_color(const std::string& color); + +bool decode_color(const std::string& color_in, ColorRGB& color_out); +bool decode_color(const std::string& color_in, ColorRGBA& color_out); + +bool decode_colors(const std::vector& colors_in, std::vector& colors_out); +bool decode_colors(const std::vector& colors_in, std::vector& colors_out); + +std::string encode_color(const ColorRGB& color); +std::string encode_color(const ColorRGBA& color); + +ColorRGB to_rgb(const ColorRGBA& other_rgba); +ColorRGBA to_rgba(const ColorRGB& other_rgb); +ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha); + +ColorRGBA picking_decode(unsigned int id); +unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b); +// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components +// were not interpolated by alpha blending or multi sampling. +unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue); + +} // namespace Slic3r + +#endif /* slic3r_Color_hpp_ */ diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp new file mode 100644 index 0000000000..cdda3097ac --- /dev/null +++ b/src/libslic3r/CutUtils.cpp @@ -0,0 +1,662 @@ +///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#include "CutUtils.hpp" +#include "Geometry.hpp" +#include "libslic3r.h" +#include "Model.hpp" +#include "TriangleMeshSlicer.hpp" +#include "TriangleSelector.hpp" +#include "ObjectID.hpp" + +#include + +namespace Slic3r { + +using namespace Geometry; + +static void apply_tolerance(ModelVolume* vol) +{ + ModelVolume::CutInfo& cut_info = vol->cut_info; + + assert(cut_info.is_connector); + if (!cut_info.is_processed) + return; + + Vec3d sf = vol->get_scaling_factor(); + + // make a "hole" wider + sf[X] += double(cut_info.radius_tolerance); + sf[Y] += double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] += double(cut_info.height_tolerance); + + vol->set_scaling_factor(sf); + + // correct offset in respect to the new depth + Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ(); + if (rot_norm.norm() != 0.0) + rot_norm.normalize(); + + double z_offset = 0.5 * static_cast(cut_info.height_tolerance); + if (cut_info.connector_type == CutConnectorType::Plug || + cut_info.connector_type == CutConnectorType::Snap) + z_offset -= 0.05; // add small Z offset to better preview + + vol->set_offset(vol->get_offset() + rot_norm * z_offset); +} + +static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + vol->set_type(type); + + vol->name = src_volume->name + suffix; + // Don't copy the config's ID. + vol->config.assign_config(src_volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != src_volume->config.id()); + vol->set_material(src_volume->material_id(), *src_volume->material()); + vol->cut_info = src_volume->cut_info; +} + +static void process_volume_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh) +{ + const auto volume_matrix = volume->get_matrix(); + + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1 * cut_transformation.get_offset()); + + // Transform the mesh by the combined transformation matrix. + // Flip the triangles in case the composite transformation is left handed. + TriangleMesh mesh(volume->mesh()); + mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); + + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); +} + +static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels) +{ + assert(volume->cut_info.is_connector); + volume->cut_info.set_processed(); + + const auto volume_matrix = volume->get_matrix(); + + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there + if (volume->cut_info.connector_type != CutConnectorType::Dowel) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = nullptr; + if (volume->cut_info.connector_type == CutConnectorType::Snap) { + TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); + + vol = upper->add_volume(std::move(mesh)); + vol->set_transformation(volume->get_transformation()); + vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); + + vol->cut_info = volume->cut_info; + vol->name = volume->name; + } + else + vol = upper->add_volume(*volume); + + vol->set_transformation(volume_matrix); + apply_tolerance(vol); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug + vol->set_type(ModelVolumeType::MODEL_PART); + } + } + else { + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject* dowel{ nullptr }; + // Clone the object to duplicate instances, materials etc. + volume->get_object()->clone_for_cut(&dowel); + + // add one more solid part same as connector if this connector is a dowel + ModelVolume* vol = dowel->add_volume(*volume); + vol->set_type(ModelVolumeType::MODEL_PART); + + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + + dowels.push_back(dowel); + } + + // Cut the dowel + apply_tolerance(volume); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh); + + // add small Z offset to better preview + upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); + lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); + + // Add cut parts to the related objects + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); + add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); + } +} + +static void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + const auto volume_matrix = instance_matrix * volume->get_matrix(); + + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Transformation(volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + upper->add_volume(*volume); + return; + } + + // Some logic for the negative volumes/connectors. Add only needed modifiers + auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); + bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) + lower->add_volume(*volume); +} + +static void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // Add required cut parts to the objects + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); + if (!lower_mesh.empty()) { + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + upper->volumes.back()->cut_info.is_from_upper = false; + } + return; + } + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) + add_cut_volume(lower_mesh, lower, volume, cut_matrix); +} + +static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, + const Transform3d& cut_matrix = Transform3d::Identity(), + bool place_on_cut = false, bool flip = false) +{ + // Reset instance transformation except offset and Z-rotation + + for (size_t i = 0; i < object->instances.size(); ++i) { + auto& obj_instance = object->instances[i]; + const double rot_z = obj_instance->get_rotation().z(); + + Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix(false, false, true)); + // add respect to mirroring + if (obj_instance->is_left_handed()) + inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1))); + + obj_instance->set_transformation(inst_trafo); + + Vec3d rotation = Vec3d::Zero(); + if (!flip && !place_on_cut) { + if ( i != src_instance_idx) + rotation[Z] = rot_z; + } + else { + Transform3d rotation_matrix = Transform3d::Identity(); + if (flip) + rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); + + if (place_on_cut) + rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse(); + + if (i != src_instance_idx) + rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; + + rotation = Transformation(rotation_matrix).get_rotation(); + } + + obj_instance->set_rotation(rotation); + } +} + + +Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes/*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts*/) + : m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes) +{ + m_model = Model(); + if (object) + m_model.add_object(*object); +} + +void Cut::post_process(ModelObject* object, ModelObjectPtrs& cut_object_ptrs, bool keep, bool place_on_cut, bool flip) +{ + if (!object) return; + + if (keep && !object->volumes.empty()) { + reset_instance_transformation(object, m_instance, m_cut_matrix, place_on_cut, flip); + cut_object_ptrs.push_back(object); + } + else + m_model.objects.push_back(object); // will be deleted in m_model.clear_objects(); +} + +void Cut::post_process(ModelObject* upper, ModelObject* lower, ModelObjectPtrs& cut_object_ptrs) +{ + post_process(upper, cut_object_ptrs, + m_attributes.has(ModelObjectCutAttribute::KeepUpper), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + + post_process(lower, cut_object_ptrs, + m_attributes.has(ModelObjectCutAttribute::KeepLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); +} + + +void Cut::finalize(const ModelObjectPtrs& objects) +{ + //clear model from temporarry objects + m_model.clear_objects(); + + // add to model result objects + m_model.objects = objects; +} + + +const ModelObjectPtrs& Cut::perform_with_plane() +{ + if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) { + m_model.clear_objects(); + return m_model.objects; + } + + ModelObject* mo = m_model.objects.front(); + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) + mo->clone_for_cut(&upper); + + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::KeepAsParts)) + mo->clone_for_cut(&lower); + + std::vector dowels; + + // Because transformations are going to be applied to meshes directly, + // we reset transformation of all instances and volumes, + // except for translation and Z-rotation on instances, which are preserved + // in the transformation matrix and not applied to the mesh transform. + + const auto instance_matrix = mo->instances[m_instance]->get_transformation().get_matrix(true); + const Transformation cut_transformation = Transformation(m_cut_matrix); + const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); + + for (ModelVolume* volume : mo->volumes) { + volume->reset_extra_facets(); + + if (!volume->is_model_part()) { + if (volume->cut_info.is_processed) + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower); + else + process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels); + } + else if (!volume->mesh().empty()) + process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower); + } + + // Post-process cut parts + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) { + m_model = Model(); + m_model.objects.push_back(upper); + return m_model.objects; + } + + ModelObjectPtrs cut_object_ptrs; + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { + reset_instance_transformation(upper, m_instance, m_cut_matrix); + cut_object_ptrs.push_back(upper); + } + else { + // Delete all modifiers which are not intersecting with solid parts bounding box + auto delete_extra_modifiers = [this](ModelObject* mo) { + if (!mo) return; + const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance); + const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix(); + + for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) + if (const ModelVolume* vol = mo->volumes[i]; + !vol->is_model_part() && !vol->is_cut_connector()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + if (!obj_bb.intersects(bb)) + mo->delete_volume(i); + } + }; + + post_process(upper, lower, cut_object_ptrs); + delete_extra_modifiers(upper); + delete_extra_modifiers(lower); + + if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + reset_instance_transformation(dowel, m_instance); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + cut_object_ptrs.push_back(dowel); + } + } + } + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + + finalize(cut_object_ptrs); + + return m_model.objects; +} + +static void distribute_modifiers_from_object(ModelObject* from_obj, const int instance_idx, ModelObject* to_obj1, ModelObject* to_obj2) +{ + auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); + auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); + const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); + + for (ModelVolume* vol : from_obj->volumes) + if (!vol->is_model_part()) { + // Don't add modifiers which are processed connectors + if (vol->cut_info.is_connector && !vol->cut_info.is_processed) + continue; + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + // Don't add modifiers which are not intersecting with solid parts + if (obj1_bb.intersects(bb)) + to_obj1->add_volume(*vol); + if (obj2_bb.intersects(bb)) + to_obj2->add_volume(*vol); + } +} + +static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) +{ + for (ModelObject* mo : objects) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + // Ensuring that volumes start with solid parts for proper slicing + mo->sort_volumes(true); + } + } +} + + +const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowels_count) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower); + + const size_t cut_parts_cnt = parts.size(); + bool has_modifiers = false; + + // Distribute SolidParts to the Upper/Lower object + for (size_t id = 0; id < cut_parts_cnt; ++id) { + if (parts[id].is_modifier) + has_modifiers = true; // modifiers will be added later to the related parts + else if (ModelObject* obj = (parts[id].selected ? upper : lower)) + obj->add_volume(*(cut_mo->volumes[id])); + } + + if (has_modifiers) { + // Distribute Modifiers to the Upper/Lower object + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + } + + ModelObjectPtrs cut_object_ptrs; + + ModelVolumePtrs& volumes = cut_mo->volumes; + if (volumes.size() == cut_parts_cnt) { + // Means that object is cut without connectors + + // Just add Upper and Lower objects to cut_object_ptrs + post_process(upper, lower, cut_object_ptrs); + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + + // replace initial objects in model with cut object + finalize(cut_object_ptrs); + } + else if (volumes.size() > cut_parts_cnt) { + // Means that object is cut with connectors + + // All volumes are distributed to Upper / Lower object, + // So we don’t need them anymore + for (size_t id = 0; id < cut_parts_cnt; id++) + delete* (volumes.begin() + id); + volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); + + // Perform cut just to get connectors + Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes); + const ModelObjectPtrs& cut_connectors_obj = cut.perform_with_plane(); + assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); + + // Connectors from upper object + for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) + upper->add_volume(*volume, volume->type()); + + // Connectors from lower object + for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) + lower->add_volume(*volume, volume->type()); + + // Add Upper and Lower objects to cut_object_ptrs + post_process(upper, lower, cut_object_ptrs); + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + + // replace initial objects in model with cut object + finalize(cut_object_ptrs); + + // Add Dowel-connectors as separate objects to model + if (cut_connectors_obj.size() >= 3) + for (size_t id = 2; id < cut_connectors_obj.size(); id++) + m_model.add_object(*cut_connectors_obj[id]); + } + + return m_model.objects; +} + + +const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + cut_mo->clone_for_cut(&lower); + + const double groove_half_depth = 0.5 * double(groove.depth); + + Model tmp_model_for_cut = Model(); + + Model tmp_model = Model(); + tmp_model.add_object(*cut_mo); + ModelObject* tmp_object = tmp_model.objects.front(); + + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { + const auto& volumes = tmp_model_for_cut.objects.front()->volumes; + for (const ModelVolume* volume : volumes) + if (volume->is_model_part()) { + if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || + (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) { + ModelVolume* new_vol = object->add_volume(*volume); + new_vol->reset_from_upper(); + } + } + }; + + auto cut = [this, add_volumes_from_cut] + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { + Cut cut(object, m_instance, cut_matrix); + + tmp_model_for_cut = Model(); + tmp_model_for_cut.add_object(*cut.perform_with_plane().front()); + assert(!tmp_model_for_cut.objects.empty()); + + object->clear_volumes(); + add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); + reset_instance_transformation(object, m_instance); + }; + + // cut by upper plane + + const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by lower plane + + const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + } + + // cut middle part with 2 angles and add parts to related upper/lower objects + + const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle)); + + // cut by angle1 plane + { + const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + + cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by angle2 plane + { + const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + + cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // apply tolerance to the middle part + { + const double h_groove_shift_tolerance = groove_half_depth - (double)groove.depth_tolerance; + + const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix; + cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + + const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance); + + const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // this part can be added to the upper object now + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + ModelObjectPtrs cut_object_ptrs; + + if (keep_as_parts) { + // add volumes from lower object to the upper, but mark them as a lower + const auto& volumes = lower->volumes; + for (const ModelVolume* volume : volumes) { + ModelVolume* new_vol = upper->add_volume(*volume); + new_vol->cut_info.is_from_upper = false; + } + + // add modifiers + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) + upper->add_volume(*volume); + + cut_object_ptrs.push_back(upper); + + // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks + cut_object_ptrs.push_back(lower); + } + else { + // add modifiers if object has any + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) { + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + break; + } + + assert(!upper->volumes.empty() && !lower->volumes.empty()); + + // Add Upper and Lower parts to cut_object_ptrs + + post_process(upper, lower, cut_object_ptrs); + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + } + + finalize(cut_object_ptrs); + + return m_model.objects; +} + +} // namespace Slic3r + diff --git a/src/libslic3r/CutUtils.hpp b/src/libslic3r/CutUtils.hpp new file mode 100644 index 0000000000..5067aaaff1 --- /dev/null +++ b/src/libslic3r/CutUtils.hpp @@ -0,0 +1,70 @@ +///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_CutUtils_hpp_ +#define slic3r_CutUtils_hpp_ + +#include "enum_bitmask.hpp" +#include "Point.hpp" +#include "Model.hpp" + +#include + +namespace Slic3r { + +using ModelObjectPtrs = std::vector; + +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; +using ModelObjectCutAttributes = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); + + +class Cut { + + Model m_model; + int m_instance; + const Transform3d m_cut_matrix; + ModelObjectCutAttributes m_attributes; + + void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip); + void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects); + void finalize(const ModelObjectPtrs& objects); + +public: + + Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts ); + ~Cut() { m_model.clear_objects(); } + + struct Groove + { + float depth{ 0.f }; + float width{ 0.f }; + float flaps_angle{ 0.f }; + float angle{ 0.f }; + float depth_init{ 0.f }; + float width_init{ 0.f }; + float flaps_angle_init{ 0.f }; + float angle_init{ 0.f }; + float depth_tolerance{ 0.1f }; + float width_tolerance{ 0.1f }; + }; + + struct Part + { + bool selected; + bool is_modifier; + }; + + const ModelObjectPtrs& perform_with_plane(); + const ModelObjectPtrs& perform_by_contour(std::vector parts, int dowels_count); + const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false); + +}; // namespace Cut + +} // namespace Slic3r + +#endif /* slic3r_CutUtils_hpp_ */ diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 391ac2d587..e6ae1edd4d 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -56,12 +56,9 @@ ExtrusionEntityCollection::operator ExtrusionPaths() const return paths; } -ExtrusionEntity* ExtrusionEntityCollection::clone() const +ExtrusionEntity *ExtrusionEntityCollection::clone() const { - ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this); - for (size_t i = 0; i < coll->entities.size(); ++i) - coll->entities[i] = this->entities[i]->clone(); - return coll; + return new ExtrusionEntityCollection(*this); } void ExtrusionEntityCollection::reverse() diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 6be38e14a1..c616d0f1b4 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -1,3 +1,8 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2020 Henner Zeller +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "../libslic3r.h" #include "../Exception.hpp" #include "../Model.hpp" @@ -741,8 +746,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) { int volume_id; int type; - float radius; - float height; float r_tolerance; float h_tolerance; }; @@ -758,10 +761,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //typedef std::map IdToAliasesMap; typedef std::vector InstancesList; typedef std::map IdToMetadataMap; - typedef std::map IdToCutObjectInfoMap; //typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; + typedef std::map IdToCutObjectInfoMap; /*typedef std::map> IdToSlaSupportPointsMap; typedef std::map> IdToSlaDrainHolesMap;*/ @@ -951,7 +954,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //IdToGeometryMap m_orig_geometries; // backup & restore CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; - IdToCutObjectInfoMap m_cut_object_infos; + IdToCutObjectInfoMap m_cut_object_infos; IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerConfigRangesMap m_layer_config_ranges; /*IdToSlaSupportPointsMap m_sla_support_points; @@ -1013,7 +1016,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); - void _extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions); + void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); @@ -1955,11 +1958,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1); if (cut_object_info != m_cut_object_infos.end()) { model_object->cut_id = cut_object_info->second.id; - + int vol_cnt = int(model_object->volumes.size()); for (auto connector : cut_object_info->second.connectors) { - assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size())); - model_object->volumes[connector.volume_id]->cut_info = - ModelVolume::CutInfo(CutConnectorType(connector.type), connector.radius, connector.height, connector.r_tolerance, connector.h_tolerance, true); + if (connector.volume_id < 0 || connector.volume_id >= vol_cnt) { + add_error("Invalid connector is found"); + continue; + } + model_object->volumes[connector.volume_id]->cut_info = + ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true); } } } @@ -2324,7 +2330,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions) { if (stat.m_uncomp_size > 0) { - std::string buffer((size_t) stat.m_uncomp_size, 0); + std::string buffer((size_t)stat.m_uncomp_size, 0); mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading cut information data to buffer"); @@ -2332,12 +2338,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } std::istringstream iss(buffer); // wrap returned xml to istringstream - pt::ptree objects_tree; + pt::ptree objects_tree; pt::read_xml(iss, objects_tree); - for (const auto &object : objects_tree.get_child("objects")) { + for (const auto& object : objects_tree.get_child("objects")) { pt::ptree object_tree = object.second; - int obj_idx = object_tree.get(".id", -1); + int obj_idx = object_tree.get(".id", -1); if (obj_idx <= 0) { add_error("Found invalid object id"); continue; @@ -2349,30 +2355,33 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; } - CutObjectBase cut_id; - std::vector connectors; + CutObjectBase cut_id; + std::vector connectors; - for (const auto &obj_cut_info : object_tree) { + for (const auto& obj_cut_info : object_tree) { if (obj_cut_info.first == "cut_id") { pt::ptree cut_id_tree = obj_cut_info.second; - cut_id = CutObjectBase(ObjectID(cut_id_tree.get(".id")), cut_id_tree.get(".check_sum"), - cut_id_tree.get(".connectors_cnt")); + cut_id = CutObjectBase(ObjectID( cut_id_tree.get(".id")), + cut_id_tree.get(".check_sum"), + cut_id_tree.get(".connectors_cnt")); } if (obj_cut_info.first == "connectors") { pt::ptree cut_connectors_tree = obj_cut_info.second; - for (const auto &cut_connector : cut_connectors_tree) { - if (cut_connector.first != "connector") continue; - pt::ptree connector_tree = cut_connector.second; - CutObjectInfo::Connector connector = {connector_tree.get(".volume_id"), connector_tree.get(".type"), - connector_tree.get(".radius", 0.f), connector_tree.get(".height", 0.f), - connector_tree.get(".r_tolerance"), connector_tree.get(".h_tolerance")}; + for (const auto& cut_connector : cut_connectors_tree) { + if (cut_connector.first != "connector") + continue; + pt::ptree connector_tree = cut_connector.second; + CutObjectInfo::Connector connector = {connector_tree.get(".volume_id"), + connector_tree.get(".type"), + connector_tree.get(".r_tolerance"), + connector_tree.get(".h_tolerance")}; connectors.emplace_back(connector); } } } - CutObjectInfo cut_info{cut_id, connectors}; - m_cut_object_infos.insert({obj_idx, cut_info}); + CutObjectInfo cut_info {cut_id, connectors}; + m_cut_object_infos.insert({ obj_idx, cut_info }); } } } @@ -5260,6 +5269,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //BBS: change volume to seperate objects bool _add_mesh_to_object_stream(std::function const &flush, ObjectData const &object_data) const; bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const; + bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); @@ -5270,7 +5280,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //BBS: add project embedded preset files bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector project_presets); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false); - bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model); bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config); bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); @@ -6233,7 +6242,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; volume_count++; if (m_share_mesh) { - auto iter = m_shared_meshes.find(volume->mesh_ptr()); + auto iter = m_shared_meshes.find(volume->mesh_ptr().get()); if (iter != m_shared_meshes.end()) { const ModelVolume* shared_volume = iter->second.second; @@ -6248,7 +6257,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; } } - const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr(), {&object_data, volume}}); + const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}}); } if (m_from_backup_save) volume_id = (volume_count << 16 | backup_id); @@ -6704,6 +6713,69 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } + bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model) + { + std::string out = ""; + pt::ptree tree; + + unsigned int object_cnt = 0; + for (const ModelObject* object : model.objects) { + object_cnt++; + if (!object->is_cut()) + continue; + pt::ptree& obj_tree = tree.add("objects.object", ""); + + obj_tree.put(".id", object_cnt); + + // Store info for cut_id + pt::ptree& cut_id_tree = obj_tree.add("cut_id", ""); + + // store cut_id atributes + cut_id_tree.put(".id", object->cut_id.id().id); + cut_id_tree.put(".check_sum", object->cut_id.check_sum()); + cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); + + int volume_idx = -1; + for (const ModelVolume* volume : object->volumes) { + ++volume_idx; + if (volume->is_cut_connector()) { + pt::ptree& connectors_tree = obj_tree.add("connectors.connector", ""); + connectors_tree.put(".volume_id", volume_idx); + connectors_tree.put(".type", int(volume->cut_info.connector_type)); + connectors_tree.put(".r_tolerance", volume->cut_info.radius_tolerance); + connectors_tree.put(".h_tolerance", volume->cut_info.height_tolerance); + } + } + } + + if (!tree.empty()) { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string for a better preview + boost::replace_all(out, ">\n \n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); + boost::replace_all(out, ">", ">\n "); + // OR just + boost::replace_all(out, "><", ">\n<"); + } + + if (!out.empty()) { + if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { + add_error("Unable to add cut information file to archive"); + return false; + } + } + + return true; + } + bool _BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model) { assert(is_decimal_separator_point()); @@ -7266,69 +7338,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model) - { - std::string out = ""; - pt::ptree tree; - - unsigned int object_cnt = 0; - for (const ModelObject *object : model.objects) { - object_cnt++; - pt::ptree &obj_tree = tree.add("objects.object", ""); - - obj_tree.put(".id", object_cnt); - - // Store info for cut_id - pt::ptree &cut_id_tree = obj_tree.add("cut_id", ""); - - // store cut_id atributes - cut_id_tree.put(".id", object->cut_id.id().id); - cut_id_tree.put(".check_sum", object->cut_id.check_sum()); - cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); - - int volume_idx = -1; - for (const ModelVolume *volume : object->volumes) { - ++volume_idx; - if (volume->is_cut_connector()) { - pt::ptree &connectors_tree = obj_tree.add("connectors.connector", ""); - connectors_tree.put(".volume_id", volume_idx); - connectors_tree.put(".type", int(volume->cut_info.connector_type)); - connectors_tree.put(".radius", volume->cut_info.radius); - connectors_tree.put(".height", volume->cut_info.height); - connectors_tree.put(".r_tolerance", volume->cut_info.radius_tolerance); - connectors_tree.put(".h_tolerance", volume->cut_info.height_tolerance); - } - } - } - - if (!tree.empty()) { - std::ostringstream oss; - pt::write_xml(oss, tree); - out = oss.str(); - - // Post processing("beautification") of the output string for a better preview - boost::replace_all(out, ">\n \n ", ">\n "); - boost::replace_all(out, ">\n ", ">\n "); - boost::replace_all(out, ">\n ", ">\n "); - boost::replace_all(out, ">", ">\n "); - // OR just - boost::replace_all(out, "><", ">\n<"); - } - - if (!out.empty()) { - if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void *) out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - add_error("Unable to add cut information file to archive"); - return false; - } - } - - return true; - } - bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config) { std::stringstream stream; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7b85a1d422..80d5263030 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2578,6 +2578,7 @@ this->placeholder_parser().set("z_offset", new ConfigOptionFloat(m_config.z_offs m_writer.extruders(), // Modifies print.m_print_statistics)); + print.m_print_statistics.initial_tool = initial_extruder_id; if (!is_bbl_printers) { file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); @@ -5448,8 +5449,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) old_retract_length = m_config.retraction_length.get_at(previous_extruder_id); old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id); old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id); - wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id]; - wipe_volume *= m_config.flush_multiplier; + if (m_config.purge_in_prime_tower) { + wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id]; + wipe_volume *= m_config.flush_multiplier; + } else { + wipe_volume = m_config.prime_volume; + } old_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(previous_extruder_id) / filament_area); old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate; //BBS: must clean m_start_gcode_filament diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 380337e5bb..772e63dd40 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -57,17 +57,20 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, std::string encoded; encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); encoded.resize(boost::beast::detail::base64::encode((void *) encoded.data(), (const void *) compressed->data, - compressed->size)); - output((boost::format("; thumbnail begin %dx%d %d\n") % data.width % data.height % encoded.size()).str().c_str()); + compressed->size)); + output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()) + .str() + .c_str()); while (encoded.size() > max_row_length) { output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); encoded = encoded.substr(max_row_length); } + // Orca write remaining ecoded data if (encoded.size() > 0) output((boost::format("; %s\n") % encoded).str().c_str()); - output("; thumbnail end\n"); + output((boost::format("; %s end\n") % compressed->tag()).str().c_str()); } throw_if_canceled(); } @@ -81,4 +84,4 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, } // namespace Slic3r::GCodeThumbnails -#endif // slic3r_GCodeThumbnails_hpp_ \ No newline at end of file +#endif // slic3r_GCodeThumbnails_hpp_ diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 2563db7bff..95c28da2ab 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -738,8 +738,16 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume() const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON); // Extract purging volumes for each extruder pair: std::vector> wipe_volumes; - for (unsigned int i = 0; i < number_of_extruders; ++i) - wipe_volumes.push_back(std::vector(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders)); + if (m_print_config_ptr->purge_in_prime_tower) { + for (unsigned int i = 0; i < number_of_extruders; ++i) + wipe_volumes.push_back( + std::vector(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders)); + } else { + // populate wipe_volumes with prime_volume + for (unsigned int i = 0; i < number_of_extruders; ++i) { + wipe_volumes.push_back(std::vector(number_of_extruders, m_print_config_ptr->prime_volume)); + } + } unsigned int current_extruder_id = -1; for (int i = 0; i < m_layer_tools.size(); ++i) { diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index ab8a54ea06..3c717a063d 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -1021,7 +1021,6 @@ void WipeTower2::toolchange_Change( // This is where we want to place the custom gcodes. We will use placeholders for this. // These will be substituted by the actual gcodes when the gcode is generated. - writer.append("[filament_end_gcode]\n"); writer.append("[change_filament_gcode]\n"); // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc) @@ -1093,7 +1092,8 @@ void WipeTower2::toolchange_Wipe( float dy = (is_first_layer() ? 1.f : m_extra_spacing) * m_perimeter_width; // Don't use the extra spacing for the first layer. // All the calculations in all other places take the spacing into account for all the layers. - const float target_speed = is_first_layer() ? m_first_layer_speed * 60.f : m_infill_speed * 60.f; + // If spare layers are excluded->if 1 or less toolchange has been done, it must be sill the first layer, too.So slow down. + const float target_speed = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers) ? m_first_layer_speed * 60.f : std::min(5400.f, m_infill_speed * 60.f); float wipe_speed = 0.33f * target_speed; // if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway) @@ -1161,9 +1161,10 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer() // Slow down on the 1st layer. - bool first_layer = is_first_layer(); - float feedrate = first_layer ? m_first_layer_speed * 60.f : m_infill_speed * 60.f; - float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth(); + // If spare layers are excluded -> if 1 or less toolchange has been done, it must be still the first layer, too. So slow down. + bool first_layer = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers); + float feedrate = first_layer ? m_first_layer_speed * 60.f : std::min(5400.f, m_infill_speed * 60.f); + float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth(); WipeTower::box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth-(current_depth-m_perimeter_width)), m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index a2c59ec17d..e57b774f34 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1,3 +1,17 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/Geometry.pm: +///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv +///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2013 Jose Luis Perez Diez +///|/ Copyright (c) 2013 Anders Sundman +///|/ Copyright (c) 2013 Jesse Vincent +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Mark Hindess +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r.h" #include "Exception.hpp" #include "Geometry.hpp" @@ -320,46 +334,103 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, return transform; } +void assemble_transform(Transform3d& transform, const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror) +{ + transform = translation * rotation * scale * mirror; +} + +Transform3d assemble_transform(const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror) +{ + Transform3d transform; + assemble_transform(transform, translation, rotation, scale, mirror); + return transform; +} + +void translation_transform(Transform3d& transform, const Vec3d& translation) +{ + transform = Transform3d::Identity(); + transform.translate(translation); +} + +Transform3d translation_transform(const Vec3d& translation) +{ + Transform3d transform; + translation_transform(transform, translation); + return transform; +} + +void rotation_transform(Transform3d& transform, const Vec3d& rotation) +{ + transform = Transform3d::Identity(); + transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); +} + +Transform3d rotation_transform(const Vec3d& rotation) +{ + Transform3d transform; + rotation_transform(transform, rotation); + return transform; +} + +void scale_transform(Transform3d& transform, double scale) +{ + return scale_transform(transform, scale * Vec3d::Ones()); +} + +void scale_transform(Transform3d& transform, const Vec3d& scale) +{ + transform = Transform3d::Identity(); + transform.scale(scale); +} + +Transform3d scale_transform(double scale) +{ + return scale_transform(scale * Vec3d::Ones()); +} + +Transform3d scale_transform(const Vec3d& scale) +{ + Transform3d transform; + scale_transform(transform, scale); + return transform; +} + Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix) { // reference: http://www.gregslabaugh.net/publications/euler.pdf Vec3d angles1 = Vec3d::Zero(); Vec3d angles2 = Vec3d::Zero(); // BBS: rotation_matrix(2, 0) may be slighterly larger than 1 due to numerical accuracy - if (std::abs(std::abs(rotation_matrix(2, 0)) - 1.0) < 1e-5 || std::abs(rotation_matrix(2, 0))>1) - { - angles1(2) = 0.0; - if (rotation_matrix(2, 0) < 0.0) // == -1.0 - { - angles1(1) = 0.5 * (double)PI; - angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2)); + if (std::abs(std::abs(rotation_matrix(2, 0)) - 1.0) < 1e-5 || std::abs(rotation_matrix(2, 0))>1) { + angles1.z() = 0.0; + if (rotation_matrix(2, 0) < 0.0) { // == -1.0 + angles1.y() = 0.5 * double(PI); + angles1.x() = angles1.z() + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2)); } - else // == 1.0 - { - angles1(1) = - 0.5 * (double)PI; - angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2)); + else { // == 1.0 + angles1.y() = - 0.5 * double(PI); + angles1.x() = - angles1.y() + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2)); } angles2 = angles1; } - else - { - angles1(1) = -::asin(rotation_matrix(2, 0)); - double inv_cos1 = 1.0 / ::cos(angles1(1)); - angles1(0) = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1); - angles1(2) = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1); + else { + angles1.y() = -::asin(rotation_matrix(2, 0)); + const double inv_cos1 = 1.0 / ::cos(angles1.y()); + angles1.x() = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1); + angles1.z() = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1); - angles2(1) = (double)PI - angles1(1); - double inv_cos2 = 1.0 / ::cos(angles2(1)); - angles2(0) = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2); - angles2(2) = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2); + angles2.y() = double(PI) - angles1.y(); + const double inv_cos2 = 1.0 / ::cos(angles2.y()); + angles2.x() = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2); + angles2.z() = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2); } // The following euristic is the best found up to now (in the sense that it works fine with the greatest number of edge use-cases) // but there are other use-cases were it does not // We need to improve it - double min_1 = angles1.cwiseAbs().minCoeff(); - double min_2 = angles2.cwiseAbs().minCoeff(); - bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm())); + const double min_1 = angles1.cwiseAbs().minCoeff(); + const double min_2 = angles2.cwiseAbs().minCoeff(); + const bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm())); return use_1 ? angles1 : angles2; } @@ -375,6 +446,14 @@ Vec3d extract_euler_angles(const Transform3d& transform) return extract_euler_angles(m); } +static Transform3d extract_rotation_matrix(const Transform3d& trafo) +{ + Matrix3d rotation; + Matrix3d scale; + trafo.computeRotationScaling(&rotation, &scale); + return Transform3d(rotation); +} + void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix) { double epsilon = 1e-5; @@ -409,28 +488,6 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl } } -Transform3d translation_transform(const Vec3d &translation) -{ - Transform3d transform = Transform3d::Identity(); - transform.translate(translation); - return transform; -} - -Transform3d rotation_transform(const Vec3d& rotation) -{ - Transform3d transform = Transform3d::Identity(); - transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); - return transform; -} - -Transformation::Flags::Flags() - : dont_translate(true) - , dont_rotate(true) - , dont_scale(true) - , dont_mirror(true) -{ -} - bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const { return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror); @@ -456,35 +513,38 @@ Transformation::Transformation(const Transform3d& transform) void Transformation::set_offset(const Vec3d& offset) { - set_offset(X, offset(0)); - set_offset(Y, offset(1)); - set_offset(Z, offset(2)); + set_offset(X, offset.x()); + set_offset(Y, offset.y()); + set_offset(Z, offset.z()); } void Transformation::set_offset(Axis axis, double offset) { - if (m_offset(axis) != offset) - { + if (m_offset(axis) != offset) { m_offset(axis) = offset; m_dirty = true; } } +Transform3d Transformation::get_rotation_matrix() const +{ + return extract_rotation_matrix(m_matrix); +} + void Transformation::set_rotation(const Vec3d& rotation) { - set_rotation(X, rotation(0)); - set_rotation(Y, rotation(1)); - set_rotation(Z, rotation(2)); + set_rotation(X, rotation.x()); + set_rotation(Y, rotation.y()); + set_rotation(Z, rotation.z()); } void Transformation::set_rotation(Axis axis, double rotation) { rotation = angle_to_0_2PI(rotation); - if (is_approx(std::abs(rotation), 2.0 * (double)PI)) + if (is_approx(std::abs(rotation), 2.0 * double(PI))) rotation = 0.0; - if (m_rotation(axis) != rotation) - { + if (m_rotation(axis) != rotation) { m_rotation(axis) = rotation; m_dirty = true; } @@ -492,15 +552,14 @@ void Transformation::set_rotation(Axis axis, double rotation) void Transformation::set_scaling_factor(const Vec3d& scaling_factor) { - set_scaling_factor(X, scaling_factor(0)); - set_scaling_factor(Y, scaling_factor(1)); - set_scaling_factor(Z, scaling_factor(2)); + set_scaling_factor(X, scaling_factor.x()); + set_scaling_factor(Y, scaling_factor.y()); + set_scaling_factor(Z, scaling_factor.z()); } void Transformation::set_scaling_factor(Axis axis, double scaling_factor) { - if (m_scaling_factor(axis) != std::abs(scaling_factor)) - { + if (m_scaling_factor(axis) != std::abs(scaling_factor)) { m_scaling_factor(axis) = std::abs(scaling_factor); m_dirty = true; } @@ -508,9 +567,9 @@ void Transformation::set_scaling_factor(Axis axis, double scaling_factor) void Transformation::set_mirror(const Vec3d& mirror) { - set_mirror(X, mirror(0)); - set_mirror(Y, mirror(1)); - set_mirror(Z, mirror(2)); + set_mirror(X, mirror.x()); + set_mirror(Y, mirror.y()); + set_mirror(Z, mirror.z()); } void Transformation::set_mirror(Axis axis, double mirror) @@ -521,8 +580,7 @@ void Transformation::set_mirror(Axis axis, double mirror) else if (abs_mirror != 1.0) mirror /= abs_mirror; - if (m_mirror(axis) != mirror) - { + if (m_mirror(axis) != mirror) { m_mirror(axis) = mirror; m_dirty = true; } @@ -540,9 +598,8 @@ void Transformation::set_from_transform(const Transform3d& transform) // we can only detect if the matrix contains a left handed reference system // in which case we reorient it back to right handed by mirroring the x axis Vec3d mirror = Vec3d::Ones(); - if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) - { - mirror(0) = -1.0; + if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) { + mirror.x() = -1.0; // remove mirror m3x3.col(0) *= -1.0; } @@ -579,8 +636,7 @@ void Transformation::reset() const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const { - if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror)) - { + if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror)) { m_matrix = Geometry::assemble_transform( dont_translate ? Vec3d::Zero() : m_offset, dont_rotate ? Vec3d::Zero() : m_rotation, @@ -609,8 +665,7 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation // Just set the inverse. out.set_from_transform(instance_transformation.get_matrix(true).inverse()); } - else if (is_rotation_ninety_degrees(instance_transformation.get_rotation())) - { + else if (is_rotation_ninety_degrees(instance_transformation.get_rotation())) { // Anisotropic scaling, rotation by multiples of ninety degrees. Eigen::Matrix3d instance_rotation_trafo = (Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) * @@ -643,8 +698,8 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i)); out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo)); - out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2)))); - out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1)); + out.set_scaling_factor(Vec3d(std::abs(scale.x()), std::abs(scale.y()), std::abs(scale.z()))); + out.set_mirror(Vec3d(scale.x() > 0 ? 1. : -1, scale.y() > 0 ? 1. : -1, scale.z() > 0 ? 1. : -1)); } else { @@ -663,19 +718,15 @@ Transform3d transform3d_from_string(const std::string& transform_str) assert(is_decimal_separator_point()); // for atof Transform3d transform = Transform3d::Identity(); - if (!transform_str.empty()) - { + if (!transform_str.empty()) { std::vector mat_elements_str; boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); - unsigned int size = (unsigned int)mat_elements_str.size(); - if (size == 16) - { + const unsigned int size = (unsigned int)mat_elements_str.size(); + if (size == 16) { unsigned int i = 0; - for (unsigned int r = 0; r < 4; ++r) - { - for (unsigned int c = 0; c < 4; ++c) - { + for (unsigned int r = 0; r < 4; ++r) { + for (unsigned int c = 0; c < 4; ++c) { transform(r, c) = ::atof(mat_elements_str[i++].c_str()); } } @@ -689,17 +740,17 @@ Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot { return // From the current coordinate system to world. - Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) * + Eigen::AngleAxisd(rot_xyz_to.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to.x(), Vec3d::UnitX()) * // From world to the initial coordinate system. - Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ()); + Eigen::AngleAxisd(-rot_xyz_from.x(), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from.z(), Vec3d::UnitZ()); } // This should only be called if it is known, that the two rotations only differ in rotation around the Z axis. double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { - Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); + const Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + const Vec3d axis = angle_axis.axis(); + const double angle = angle_axis.angle(); #ifndef NDEBUG if (std::abs(angle) > 1e-8) { assert(std::abs(axis.x()) < 1e-8); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 8eb6195a10..4f6511ff6f 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -1,3 +1,18 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Lukáš Hejl @hejllukas +///|/ Copyright (c) 2017 Eyal Soha @eyal0 +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/Geometry.pm: +///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv +///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2013 Jose Luis Perez Diez +///|/ Copyright (c) 2013 Anders Sundman +///|/ Copyright (c) 2013 Jesse Vincent +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Mark Hindess +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_Geometry_hpp_ #define slic3r_Geometry_hpp_ @@ -324,7 +339,8 @@ bool arrange( // 4) rotate Y // 5) rotate Z // 6) translate -void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), + const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); // Returns the transform obtained by assembling the given transformations in the following order: // 1) mirror @@ -333,7 +349,45 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d // 4) rotate Y // 5) rotate Z // 6) translate -Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); +Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), + const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); + +// Sets the given transform by multiplying the given transformations in the following order: +// T = translation * rotation * scale * mirror +void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d::Identity(), + const Transform3d& rotation = Transform3d::Identity(), const Transform3d& scale = Transform3d::Identity(), + const Transform3d& mirror = Transform3d::Identity()); + +// Returns the transform obtained by multiplying the given transformations in the following order: +// T = translation * rotation * scale * mirror +Transform3d assemble_transform(const Transform3d& translation = Transform3d::Identity(), const Transform3d& rotation = Transform3d::Identity(), + const Transform3d& scale = Transform3d::Identity(), const Transform3d& mirror = Transform3d::Identity()); + +// Sets the given transform by assembling the given translation +void translation_transform(Transform3d& transform, const Vec3d& translation); + +// Returns the transform obtained by assembling the given translation +Transform3d translation_transform(const Vec3d& translation); + +// Sets the given transform by assembling the given rotations in the following order: +// 1) rotate X +// 2) rotate Y +// 3) rotate Z +void rotation_transform(Transform3d& transform, const Vec3d& rotation); + +// Returns the transform obtained by assembling the given rotations in the following order: +// 1) rotate X +// 2) rotate Y +// 3) rotate Z +Transform3d rotation_transform(const Vec3d& rotation); + +// Sets the given transform by assembling the given scale factors +void scale_transform(Transform3d& transform, double scale); +void scale_transform(Transform3d& transform, const Vec3d& scale); + +// Returns the transform obtained by assembling the given scale factors +Transform3d scale_transform(double scale); +Transform3d scale_transform(const Vec3d& scale); // Returns the euler angles extracted from the given rotation matrix // Warning -> The matrix should not contain any scale or shear !!! @@ -346,40 +400,29 @@ Vec3d extract_euler_angles(const Transform3d& transform); // get rotation from two vectors. // Default output is axis-angle. If rotation_matrix pointer is provided, also output rotation matrix // Euler angles can be obtained by extract_euler_angles() -void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix = nullptr); - -// Returns the transform obtained by assembling the given translation -Transform3d translation_transform(const Vec3d &translation); - -// Returns the transform obtained by assembling the given rotations in the following order: -// 1) rotate X -// 2) rotate Y -// 3) rotate Z -Transform3d rotation_transform(const Vec3d &rotation); +void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d &rotation_axis, double &phi, Matrix3d *rotation_matrix = nullptr); class Transformation { struct Flags { - bool dont_translate; - bool dont_rotate; - bool dont_scale; - bool dont_mirror; - - Flags(); + bool dont_translate{ true }; + bool dont_rotate{ true }; + bool dont_scale{ true }; + bool dont_mirror{ true }; bool needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const; void set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror); }; - Vec3d m_offset; // In unscaled coordinates - Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point - Vec3d m_scaling_factor; // Scaling factors along the three axes - Vec3d m_mirror; // Mirroring along the three axes + Vec3d m_offset{ Vec3d::Zero() }; // In unscaled coordinates + Vec3d m_rotation{ Vec3d::Zero() }; // Rotation around the three axes, in radians around mesh center point + Vec3d m_scaling_factor{ Vec3d::Ones() }; // Scaling factors along the three axes + Vec3d m_mirror{ Vec3d::Ones() }; // Mirroring along the three axes - mutable Transform3d m_matrix; + mutable Transform3d m_matrix{ Transform3d::Identity() }; mutable Flags m_flags; - mutable bool m_dirty; + mutable bool m_dirty{ false }; public: Transformation(); @@ -397,6 +440,8 @@ public: const Vec3d& get_rotation() const { return m_rotation; } double get_rotation(Axis axis) const { return m_rotation(axis); } + Transform3d get_rotation_matrix() const; + void set_rotation(const Vec3d& rotation); void set_rotation(Axis axis, double rotation); @@ -457,7 +502,7 @@ extern double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to // Is the angle close to a multiple of 90 degrees? inline bool is_rotation_ninety_degrees(double a) { - a = fmod(std::abs(a), 0.5 * M_PI); + a = fmod(std::abs(a), 0.5 * PI); if (a > 0.25 * PI) a = 0.5 * PI - a; return a < 0.001; diff --git a/src/libslic3r/Geometry/Circle.cpp b/src/libslic3r/Geometry/Circle.cpp index 4d7c38ccc2..012b240f8a 100644 --- a/src/libslic3r/Geometry/Circle.cpp +++ b/src/libslic3r/Geometry/Circle.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Circle.hpp" #include "../Polygon.hpp" @@ -108,7 +112,7 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles) return out; } -Circled circle_ransac(const Vec2ds& input, size_t iterations) +Circled circle_ransac(const Vec2ds& input, size_t iterations, double* min_error) { if (input.size() < 3) return Circled::make_invalid(); @@ -132,6 +136,8 @@ Circled circle_ransac(const Vec2ds& input, size_t iterations) circle_best = c; } } + if (min_error) + *min_error = err_min; return circle_best; } diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index 39973d916d..a192cc2fd6 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_Geometry_Circle_hpp_ #define slic3r_Geometry_Circle_hpp_ @@ -102,7 +106,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20 Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20); // Find circle using RANSAC randomized algorithm. -Circled circle_ransac(const Vec2ds& input, size_t iterations = 20); +Circled circle_ransac(const Vec2ds& input, size_t iterations = 20, double* min_error = nullptr); // Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon. template diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp new file mode 100644 index 0000000000..2e6156a88e --- /dev/null +++ b/src/libslic3r/Measure.cpp @@ -0,0 +1,1255 @@ +///|/ Copyright (c) Prusa Research 2022 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "libslic3r/libslic3r.h" +#include "Measure.hpp" +#include "MeasureUtils.hpp" + +#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/SurfaceMesh.hpp" + + +#include +#include + +#define DEBUG_EXTRACT_ALL_FEATURES_AT_ONCE 0 + +namespace Slic3r { +namespace Measure { + + +constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it + +static std::tuple get_center_and_radius(const std::vector& points, const Transform3d& trafo, const Transform3d& trafo_inv) +{ + Vec2ds out; + double z = 0.; + for (const Vec3d& pt : points) { + Vec3d pt_transformed = trafo * pt; + z = pt_transformed.z(); + out.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + const int iter = points.size() < 10 ? 2 : + points.size() < 100 ? 4 : + 6; + + double error = std::numeric_limits::max(); + auto circle = Geometry::circle_ransac(out, iter, &error); + + return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error); +} + + + +static std::array orthonormal_basis(const Vec3d& v) +{ + std::array ret; + ret[2] = v.normalized(); + int index; + ret[2].cwiseAbs().maxCoeff(&index); + switch (index) + { + case 0: { ret[0] = Vec3d(ret[2].y(), -ret[2].x(), 0.0).normalized(); break; } + case 1: { ret[0] = Vec3d(0.0, ret[2].z(), -ret[2].y()).normalized(); break; } + case 2: { ret[0] = Vec3d(-ret[2].z(), 0.0, ret[2].x()).normalized(); break; } + } + ret[1] = ret[2].cross(ret[0]).normalized(); + return ret; +} + + + +class MeasuringImpl { +public: + explicit MeasuringImpl(const indexed_triangle_set& its); + struct PlaneData { + std::vector facets; + std::vector> borders; // FIXME: should be in fact local in update_planes() + std::vector surface_features; + Vec3d normal; + float area; + bool features_extracted = false; + }; + + std::optional get_feature(size_t face_idx, const Vec3d& point); + int get_num_of_planes() const; + const std::vector& get_plane_triangle_indices(int idx) const; + const std::vector& get_plane_features(unsigned int plane_id); + const indexed_triangle_set& get_its() const; + +private: + void update_planes(); + void extract_features(int plane_idx); + + std::vector m_planes; + std::vector m_face_to_plane; + indexed_triangle_set m_its; +}; + + + + + + +MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) +: m_its(its) +{ + update_planes(); + + // Extracting features will be done as needed. + // To extract all planes at once, run the following: +#if DEBUG_EXTRACT_ALL_FEATURES_AT_ONCE + for (int i=0; i face_normals = its_face_normals(m_its); + const std::vector face_neighbors = its_face_neighbors(m_its); + std::vector facet_queue(num_of_facets, 0); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + size_t seed_facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + m_planes.clear(); + m_planes.reserve(num_of_facets / 5); // empty plane data object is quite lightweight, let's save the initial reallocations + + + // First go through all the triangles and fill in m_planes vector. For each "plane" + // detected on the model, it will contain list of facets that are part of it. + // We will also fill in m_face_to_plane, which contains index into m_planes + // for each of the source facets. + while (1) { + // Find next unvisited triangle: + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (m_face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + m_face_to_plane[seed_facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (seed_facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { +// const Vec3i& face = m_its.indices[facet_idx]; + + m_face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && m_face_to_plane[neighbor_idx] == size_t(-1)) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + + m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); + } + + // Check that each facet is part of one of the planes. + assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + // Now we will walk around each of the planes and save vertices which form the border. + const SurfaceMesh sm(m_its); + + const auto& face_to_plane = m_face_to_plane; + auto& planes = m_planes; + + tbb::parallel_for(tbb::blocked_range(0, m_planes.size()), + [&planes, &face_to_plane, &face_neighbors, &sm](const tbb::blocked_range& range) { + for (size_t plane_id = range.begin(); plane_id != range.end(); ++plane_id) { + + const auto& facets = planes[plane_id].facets; + planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + + for (int face_id=0; face_id& last_border = planes[plane_id].borders.back(); + last_border.reserve(4); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + if (he.is_invalid()) + goto PLANE_FAILURE; + + // For broken meshes, the iteration might never get back to he_orig. + // Remember all halfedges we saw to break out of such infinite loops. + boost::container::small_vector he_seen; + + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) { + he_seen.emplace_back(he); + he = sm.next_around_target(he); + if (he.is_invalid() || std::find(he_seen.begin(), he_seen.end(), he) != he_seen.end()) + goto PLANE_FAILURE; + } + he = sm.opposite(he); + if (he.is_invalid()) + goto PLANE_FAILURE; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh. + goto PLANE_FAILURE; + + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + last_border.resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + last_border.emplace_back(sm.point(sm.source(he)).cast()); + + // In case of broken meshes, this loop might be infinite. Break + // out in case it is clearly going bad. + if (last_border.size() > 3*facets.size()+1) + goto PLANE_FAILURE; + + } while (he != he_start); + + if (last_border.size() == 1) + planes[plane_id].borders.pop_back(); + else { + assert(last_border.front() == last_border.back()); + last_border.pop_back(); + } + } + } + continue; // There was no failure. + + PLANE_FAILURE: + planes[plane_id].borders.clear(); + }}); + m_planes.shrink_to_fit(); +} + + + + + + +void MeasuringImpl::extract_features(int plane_idx) +{ + assert(! m_planes[plane_idx].features_extracted); + + PlaneData& plane = m_planes[plane_idx]; + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); + const Transform3d trafo_inv = trafo.inverse(); + + std::vector angles; // placed in outer scope to prevent reallocations + std::vector lengths; + + for (const std::vector& border : plane.borders) { + if (border.size() <= 1) + continue; + + bool done = false; + + if (border.size() > 4) { + const auto& [center, radius, err] = get_center_and_radius(border, trafo, trafo_inv); + + if (err < 0.05) { + // The whole border is one circle. Just add it into the list of features + // and we are done. + + bool is_polygon = border.size()>4 && border.size()<=8; + bool lengths_match = std::all_of(border.begin()+2, border.end(), [is_polygon](const Vec3d& pt) { + return Slic3r::is_approx((pt - *((&pt)-1)).squaredNorm(), (*((&pt)-1) - *((&pt)-2)).squaredNorm(), is_polygon ? 0.01 : 0.01); + }); + + if (lengths_match && (is_polygon || border.size() > 8)) { + if (is_polygon) { + // This is a polygon, add the separate edges with the center. + for (int j=0; j int { + assert(std::abs(offset) < border_size); + int out = idx+offset; + if (out >= border_size) + out = out - border_size; + else if (out < 0) + out = border_size + out; + + return out; + }; + + // First calculate angles at all the vertices. + angles.clear(); + lengths.clear(); + int first_different_angle_idx = 0; + for (int i=0; i M_PI) + angle = 2*M_PI - angle; + + angles.push_back(angle); + lengths.push_back(v2.norm()); + if (first_different_angle_idx == 0 && angles.size() > 1) { + if (! are_angles_same(angles.back(), angles[angles.size()-2])) + first_different_angle_idx = angles.size()-1; + } + } + assert(border.size() == angles.size()); + assert(border.size() == lengths.size()); + + // First go around the border and pick what might be circular segments. + // Save pair of indices to where such potential segments start and end. + // Also remember the length of these segments. + int start_idx = -1; + bool circle = false; + bool first_iter = true; + std::vector circles; + std::vector edges; + std::vector> circles_idxs; + //std::vector circles_lengths; + std::vector single_circle; // could be in loop-scope, but reallocations + double single_circle_length = 0.; + int first_pt_idx = offset_to_index(first_different_angle_idx, 1); + int i = first_pt_idx; + while (i != first_pt_idx || first_iter) { + if (are_angles_same(angles[i], angles[offset_to_index(i,-1)]) + && i != offset_to_index(first_pt_idx, -1) // not the last point + && i != start_idx ) { + // circle + if (! circle) { + circle = true; + single_circle.clear(); + single_circle_length = 0.; + start_idx = offset_to_index(i, -2); + single_circle = { border[start_idx], border[offset_to_index(start_idx,1)] }; + single_circle_length += lengths[offset_to_index(i, -1)]; + } + single_circle.emplace_back(border[i]); + single_circle_length += lengths[i]; + } else { + if (circle && single_circle.size() >= 5) { // Less than 5 vertices? Not a circle. + single_circle.emplace_back(border[i]); + single_circle_length += lengths[i]; + + bool accept_circle = true; + { + // Check that lengths of internal (!!!) edges match. + int j = offset_to_index(start_idx, 3); + while (j != i) { + if (! are_lengths_same(lengths[offset_to_index(j,-1)], lengths[j])) { + accept_circle = false; + break; + } + j = offset_to_index(j, 1); + } + } + + if (accept_circle) { + const auto& [center, radius, err] = get_center_and_radius(single_circle, trafo, trafo_inv); + + // Check that the fit went well. The tolerance is high, only to + // reject complete failures. + accept_circle &= err < 0.05; + + // If the segment subtends less than 90 degrees, throw it away. + accept_circle &= single_circle_length / radius > 0.9*M_PI/2.; + + if (accept_circle) { + // Add the circle and remember indices into borders. + circles_idxs.emplace_back(start_idx, i); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius)); + } + } + } + circle = false; + } + // Take care of the wrap around. + first_iter = false; + i = offset_to_index(i, 1); + } + + // We have the circles. Now go around again and pick edges, while jumping over circles. + if (circles_idxs.empty()) { + // Just add all edges. + for (int i=1; i 1 || circles_idxs.front().first != circles_idxs.front().second) { + // There is at least one circular segment. Start at its end and add edges until the start of the next one. + int i = circles_idxs.front().second; + int circle_idx = 1; + while (true) { + i = offset_to_index(i, 1); + edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[offset_to_index(i,-1)], border[i])); + if (circle_idx < int(circles_idxs.size()) && i == circles_idxs[circle_idx].first) { + i = circles_idxs[circle_idx].second; + ++circle_idx; + } + if (i == circles_idxs.front().first) + break; + } + } + + // Merge adjacent edges where needed. + assert(std::all_of(edges.begin(), edges.end(), + [](const SurfaceFeature& f) { return f.get_type() == SurfaceFeatureType::Edge; })); + for (int i=edges.size()-1; i>=0; --i) { + const auto& [first_start, first_end] = edges[i==0 ? edges.size()-1 : i-1].get_edge(); + const auto& [second_start, second_end] = edges[i].get_edge(); + + if (Slic3r::is_approx(first_end, second_start) + && Slic3r::is_approx((first_end-first_start).normalized().dot((second_end-second_start).normalized()), 1.)) { + // The edges have the same direction and share a point. Merge them. + edges[i==0 ? edges.size()-1 : i-1] = SurfaceFeature(SurfaceFeatureType::Edge, first_start, second_end); + edges.erase(edges.begin() + i); + } + } + + // Now move the circles and edges into the feature list for the plane. + assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Circle; + })); + assert(std::all_of(edges.begin(), edges.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Edge; + })); + plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), + std::make_move_iterator(circles.end())); + plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(edges.begin()), + std::make_move_iterator(edges.end())); + } + } + + // The last surface feature is the plane itself. + Vec3d cog = Vec3d::Zero(); + size_t counter = 0; + for (const std::vector& b : plane.borders) { + for (size_t i = 1; i < b.size(); ++i) { + cog += b[i]; + ++counter; + } + } + cog /= double(counter); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, + plane.normal, cog, std::optional(), plane_idx + 0.0001)); + + plane.borders.clear(); + plane.borders.shrink_to_fit(); + + plane.features_extracted = true; +} + + + + + + + + +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) +{ + if (face_idx >= m_face_to_plane.size()) + return std::optional(); + + const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; + + if (! plane.features_extracted) + extract_features(m_face_to_plane[face_idx]); + + size_t closest_feature_idx = size_t(-1); + double min_dist = std::numeric_limits::max(); + + MeasurementResult res; + SurfaceFeature point_sf(point); + + assert(plane.surface_features.empty() || plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + + for (size_t i=0; idist; + if (dist < feature_hover_limit && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature_idx = i; + } + } + } + + if (closest_feature_idx != size_t(-1)) { + const SurfaceFeature& f = plane.surface_features[closest_feature_idx]; + if (f.get_type() == SurfaceFeatureType::Edge) { + // If this is an edge, check if we are not close to the endpoint. If so, + // we will include the endpoint as well. Close = 10% of the lenghth of + // the edge, clamped between 0.025 and 0.5 mm. + const auto& [sp, ep] = f.get_edge(); + double len_sq = (ep-sp).squaredNorm(); + double limit_sq = std::max(0.025*0.025, std::min(0.5*0.5, 0.1 * 0.1 * len_sq)); + + if ((point-sp).squaredNorm() < limit_sq) + return std::make_optional(SurfaceFeature(sp)); + if ((point-ep).squaredNorm() < limit_sq) + return std::make_optional(SurfaceFeature(ep)); + } + return std::make_optional(f); + } + + // Nothing detected, return the plane as a whole. + assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + return std::make_optional(plane.surface_features.back()); +} + + + + + +int MeasuringImpl::get_num_of_planes() const +{ + return (m_planes.size()); +} + + + +const std::vector& MeasuringImpl::get_plane_triangle_indices(int idx) const +{ + assert(idx >= 0 && idx < int(m_planes.size())); + return m_planes[idx].facets; +} + +const std::vector& MeasuringImpl::get_plane_features(unsigned int plane_id) +{ + assert(plane_id < m_planes.size()); + if (! m_planes[plane_id].features_extracted) + extract_features(plane_id); + return m_planes[plane_id].surface_features; +} + +const indexed_triangle_set& MeasuringImpl::get_its() const +{ + return this->m_its; +} + + + + + + + + + + + +Measuring::Measuring(const indexed_triangle_set& its) +: priv{std::make_unique(its)} +{} + +Measuring::~Measuring() {} + + + +std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const +{ + return priv->get_feature(face_idx, point); +} + + +int Measuring::get_num_of_planes() const +{ + return priv->get_num_of_planes(); +} + + +const std::vector& Measuring::get_plane_triangle_indices(int idx) const +{ + return priv->get_plane_triangle_indices(idx); +} + +const std::vector& Measuring::get_plane_features(unsigned int plane_id) const +{ + return priv->get_plane_features(plane_id); +} + +const indexed_triangle_set& Measuring::get_its() const +{ + return priv->get_its(); +} + +const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; + +static AngleAndEdges angle_edge_edge(const std::pair& e1, const std::pair& e2) +{ + if (are_parallel(e1, e2)) + return AngleAndEdges::Dummy; + + Vec3d e1_unit = edge_direction(e1.first, e1.second); + Vec3d e2_unit = edge_direction(e2.first, e2.second); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + std::pair out_e1 = e1; + std::pair out_e2 = e2; + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e11_proj, e12_proj); + std::swap(out_e1.first, out_e1.second); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e21_proj, e22_proj); + std::swap(out_e2.first, out_e2.second); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return { angle, center, out_e1, out_e2, radius, coplanar }; +} + +static AngleAndEdges angle_edge_plane(const std::pair& e, const std::tuple& p) +{ + const auto& [idx, normal, origin] = p; + Vec3d e1e2_unit = edge_direction(e); + if (are_perpendicular(e1e2_unit, normal)) + return AngleAndEdges::Dummy; + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // then verify edge direction and revert it, if needed + Vec3d e1 = e.first; + Vec3d e2 = e.second; + if ((e1 - inters).squaredNorm() > (e2 - inters).squaredNorm()) { + std::swap(e1, e2); + e1e2_unit = -e1e2_unit; + } + + if (are_parallel(e1e2_unit, normal)) { + const std::array basis = orthonormal_basis(e1e2_unit); + const double radius = (0.5 * (e1 + e2) - inters).norm(); + const Vec3d edge_on_plane_dir = (basis[1].dot(origin - inters) >= 0.0) ? basis[1] : -basis[1]; + std::pair edge_on_plane = std::make_pair(inters, inters + radius * edge_on_plane_dir); + if (!inters.isApprox(e1)) { + edge_on_plane.first += radius * edge_on_plane_dir; + edge_on_plane.second += radius * edge_on_plane_dir; + } + return AngleAndEdges(0.5 * double(PI), inters, std::make_pair(e1, e2), edge_on_plane, radius, inters.isApprox(e1)); + } + + const Vec3d e1e2 = e2 - e1; + const double e1e2_len = e1e2.norm(); + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; + + AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); + ret.radius = (inters - 0.5 * (e1 + e2)).norm(); + return ret; +} + +static AngleAndEdges angle_plane_plane(const std::tuple& p1, const std::tuple& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + + // are planes parallel ? + if (are_parallel(normal1, normal2)) + return AngleAndEdges::Dummy; + + auto intersection_plane_plane = [](const Vec3d& n1, const Vec3d& o1, const Vec3d& n2, const Vec3d& o2) { + Eigen::MatrixXd m(2, 3); + m << n1.x(), n1.y(), n1.z(), n2.x(), n2.y(), n2.z(); + Eigen::VectorXd b(2); + b << o1.dot(n1), o2.dot(n2); + Eigen::VectorXd x = m.colPivHouseholderQr().solve(b); + return std::make_pair(n1.cross(n2).normalized(), Vec3d(x(0), x(1), x(2))); + }; + + // Calculate intersection line between planes + const auto [intersection_line_direction, intersection_line_origin] = intersection_plane_plane(normal1, origin1, normal2, origin2); + + // Project planes' origin on intersection line + const Eigen::ParametrizedLine intersection_line = Eigen::ParametrizedLine(intersection_line_origin, intersection_line_direction); + const Vec3d origin1_proj = intersection_line.projection(origin1); + const Vec3d origin2_proj = intersection_line.projection(origin2); + + // Calculate edges on planes + const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); + const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); + const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); + const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; + const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; + + AngleAndEdges ret = angle_edge_edge(edge_on_plane1, edge_on_plane2); + ret.radius = radius; + return ret; +} + + + + + + + +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring) +{ + assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); + + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature& f1 = swap ? b : a; + const SurfaceFeature& f2 = swap ? a : b; + + MeasurementResult result; + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + if (f1.get_type() == SurfaceFeatureType::Point) { + if (f2.get_type() == SurfaceFeatureType::Point) { + Vec3d diff = (f2.get_point() - f1.get_point()); + result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); + result.distance_xyz = diff.cwiseAbs(); + + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Edge) { + const auto [s,e] = f2.get_edge(); + const Eigen::ParametrizedLine line(s, (e-s).normalized()); + const double dist_inf = line.distance(f1.get_point()); + const Vec3d proj = line.projection(f1.get_point()); + const double len_sq = (e-s).squaredNorm(); + const double dist_start_sq = (proj-s).squaredNorm(); + const double dist_end_sq = (proj-e).squaredNorm(); + if (dist_start_sq < len_sq && dist_end_sq < len_sq) { + // projection falls on the line - the strict distance is the same as infinite + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); + } else { // the result is the closer of the endpoints + const bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + sqr(dist_inf)), f1.get_point(), s_is_closer ? s : e}); + } + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + // Find a plane containing normal, center and the point. + const auto [c, radius, n] = f2.get_circle(); + const Eigen::Hyperplane circle_plane(n, c); + const Vec3d proj = circle_plane.projection(f1.get_point()); + if (proj.isApprox(c)) { + const Vec3d p_on_circle = c + radius * get_orthogonal(n, true); + result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle }); + } + else { + const Eigen::Hyperplane circle_plane(n, c); + const Vec3d proj = circle_plane.projection(f1.get_point()); + const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + + (f1.get_point() - proj).squaredNorm()); + + const Vec3d p_on_circle = c + radius * (proj - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO + } + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + const auto [idx, normal, pt] = f2.get_plane(); + Eigen::Hyperplane plane(normal, pt); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO + // TODO: result.distance_strict = + } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + } + else if (f1.get_type() == SurfaceFeatureType::Edge) { + if (f2.get_type() == SurfaceFeatureType::Edge) { + std::vector distances; + + auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); + double distance = res.distance_strict->dist; + Vec3d v2 = res.distance_strict->to; + + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(distance, v, v2); + }; + + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); + + distances.emplace_back((e2.first - e1.first).norm(), e1.first, e2.first); + distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); + distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); + distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(*it); + + result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge()); + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + const std::pair e = f1.get_edge(); + const auto& [center, radius, normal] = f2.get_circle(); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = e1e2.normalized(); + + std::vector distances; + distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); + distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); + + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + assert(measuring != nullptr); + + const auto [from, to] = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + const Vec3d edge_unit = (to - from).normalized(); + if (are_perpendicular(edge_unit, normal)) { + std::vector distances; + const Eigen::Hyperplane plane(normal, origin); + distances.push_back(DistAndPoints{ plane.absDistance(from), from, plane.projection(from) }); + distances.push_back(DistAndPoints{ plane.absDistance(to), to, plane.projection(to) }); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + else { + const std::vector& plane_features = measuring->get_plane_features(idx); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + } + result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); + } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + } else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + const auto [c0, r0, n0] = f1.get_circle(); + const auto [c1, r1, n1] = f2.get_circle(); + + // The following code is an adaptation of the algorithm found in: + // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h + // and described in: + // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf + + struct ClosestInfo + { + double sqrDistance{ 0.0 }; + Vec3d circle0Closest{ Vec3d::Zero() }; + Vec3d circle1Closest{ Vec3d::Zero() }; + + inline bool operator < (const ClosestInfo& other) const { return sqrDistance < other.sqrDistance; } + }; + std::array candidates{}; + + const double zero = 0.0; + + const Vec3d D = c1 - c0; + + if (!are_parallel(n0, n1)) { + // Get parameters for constructing the degree-8 polynomial phi. + const double one = 1.0; + const double two = 2.0; + const double r0sqr = sqr(r0); + const double r1sqr = sqr(r1); + + // Compute U1 and V1 for the plane of circle1. + const std::array basis = orthonormal_basis(n1); + const Vec3d U1 = basis[0]; + const Vec3d V1 = basis[1]; + + // Construct the polynomial phi(cos(theta)). + const Vec3d N0xD = n0.cross(D); + const Vec3d N0xU1 = n0.cross(U1); + const Vec3d N0xV1 = n0.cross(V1); + const double a0 = r1 * D.dot(U1); + const double a1 = r1 * D.dot(V1); + const double a2 = N0xD.dot(N0xD); + const double a3 = r1 * N0xD.dot(N0xU1); + const double a4 = r1 * N0xD.dot(N0xV1); + const double a5 = r1sqr * N0xU1.dot(N0xU1); + const double a6 = r1sqr * N0xU1.dot(N0xV1); + const double a7 = r1sqr * N0xV1.dot(N0xV1); + Polynomial1 p0{ a2 + a7, two * a3, a5 - a7 }; + Polynomial1 p1{ two * a4, two * a6 }; + Polynomial1 p2{ zero, a1 }; + Polynomial1 p3{ -a0 }; + Polynomial1 p4{ -a6, a4, two * a6 }; + Polynomial1 p5{ -a3, a7 - a5 }; + Polynomial1 tmp0{ one, zero, -one }; + Polynomial1 tmp1 = p2 * p2 + tmp0 * p3 * p3; + Polynomial1 tmp2 = two * p2 * p3; + Polynomial1 tmp3 = p4 * p4 + tmp0 * p5 * p5; + Polynomial1 tmp4 = two * p4 * p5; + Polynomial1 p6 = p0 * tmp1 + tmp0 * p1 * tmp2 - r0sqr * tmp3; + Polynomial1 p7 = p0 * tmp2 + p1 * tmp1 - r0sqr * tmp4; + + // Parameters for polynomial root finding. The roots[] array + // stores the roots. We need only the unique ones, which is + // the responsibility of the set uniqueRoots. The pairs[] + // array stores the (cosine,sine) information mentioned in the + // PDF. TODO: Choose the maximum number of iterations for root + // finding based on specific polynomial data? + const uint32_t maxIterations = 128; + int32_t degree = 0; + size_t numRoots = 0; + std::array roots{}; + std::set uniqueRoots{}; + size_t numPairs = 0; + std::array, 16> pairs{}; + double temp = zero; + double sn = zero; + + if (p7.GetDegree() > 0 || p7[0] != zero) { + // H(cs,sn) = p6(cs) + sn * p7(cs) + Polynomial1 phi = p6 * p6 - tmp0 * p7 * p7; + degree = static_cast(phi.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &phi[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = p7(cs); + if (temp != zero) { + sn = -p6(cs) / temp; + pairs[numPairs++] = std::make_pair(cs, sn); + } + else { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + } + else { + // H(cs,sn) = p6(cs) + degree = static_cast(p6.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &p6[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + + for (size_t i = 0; i < numPairs; ++i) { + ClosestInfo& info = candidates[i]; + Vec3d delta = D + r1 * (pairs[i].first * U1 + pairs[i].second * V1); + info.circle1Closest = c0 + delta; + const double N0dDelta = n0.dot(delta); + const double lenN0xDelta = n0.cross(delta).norm(); + if (lenN0xDelta > 0.0) { + const double diff = lenN0xDelta - r0; + info.sqrDistance = sqr(N0dDelta) + sqr(diff); + delta -= N0dDelta * n0; + delta.normalize(); + info.circle0Closest = c0 + r0 * delta; + } + else { + const Vec3d r0U0 = r0 * get_orthogonal(n0, true); + const Vec3d diff = delta - r0U0; + info.sqrDistance = diff.dot(diff); + info.circle0Closest = c0 + r0U0; + } + } + + std::sort(candidates.begin(), candidates.begin() + numPairs); + } + else { + ClosestInfo& info = candidates[0]; + + const double N0dD = n0.dot(D); + const Vec3d normProj = N0dD * n0; + const Vec3d compProj = D - normProj; + Vec3d U = compProj; + const double d = U.norm(); + U.normalize(); + + // The configuration is determined by the relative location of the + // intervals of projection of the circles on to the D-line. + // Circle0 projects to [-r0,r0] and circle1 projects to + // [d-r1,d+r1]. + const double dmr1 = d - r1; + double distance; + if (dmr1 >= r0) { + // d >= r0 + r1 + // The circles are separated (d > r0 + r1) or tangent with one + // outside the other (d = r0 + r1). + distance = dmr1 - r0; + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // d < r0 + r1 + // The cases implicitly use the knowledge that d >= 0. + const double dpr1 = d + r1; + if (dpr1 <= r0) { + // Circle1 is inside circle0. + distance = r0 - dpr1; + if (d > 0.0) { + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else if (dmr1 <= -r0) { + // Circle0 is inside circle1. + distance = -r0 - dmr1; + if (d > 0.0) { + info.circle0Closest = c0 - r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else { + distance = (c1 - c0).norm(); + info.circle0Closest = c0; + info.circle1Closest = c1; + } + } + + info.sqrDistance = distance * distance + N0dD * N0dD; + } + + result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO + /////////////////////////////////////////////////////////////////////////// + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + assert(measuring != nullptr); + + const auto [center, radius, normal1] = f1.get_circle(); + const auto [idx2, normal2, origin2] = f2.get_plane(); + + const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane(normal1, center).absDistance(origin2) < EPSILON; + if (!coplanar) { + const std::vector& plane_features = measuring->get_plane_features(idx2); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + } + } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + } else if (f1.get_type() == SurfaceFeatureType::Plane) { + const auto [idx1, normal1, pt1] = f1.get_plane(); + const auto [idx2, normal2, pt2] = f2.get_plane(); + + if (are_parallel(normal1, normal2)) { + // The planes are parallel, calculate distance. + const Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO + } + else + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); + } + + return result; +} + + + + + + + + +} // namespace Measure +} // namespace Slic3r + diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp new file mode 100644 index 0000000000..dcccafb70d --- /dev/null +++ b/src/libslic3r/Measure.hpp @@ -0,0 +1,200 @@ +///|/ Copyright (c) Prusa Research 2022 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef Slic3r_Measure_hpp_ +#define Slic3r_Measure_hpp_ + +#include +#include + +#include "Point.hpp" + + +struct indexed_triangle_set; + + + +namespace Slic3r { + +class TriangleMesh; + +namespace Measure { + + +enum class SurfaceFeatureType : int { + Undef = 0, + Point = 1 << 0, + Edge = 1 << 1, + Circle = 1 << 2, + Plane = 1 << 3 +}; + +class SurfaceFeature { +public: + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) + : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} + + explicit SurfaceFeature(const Vec3d& pt) + : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} + + // Get type of this feature. + SurfaceFeatureType get_type() const { return m_type; } + + // For points, return the point. + Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; } + + // For edges, return start and end. + std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } + + // For circles, return center, radius and normal. + std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } + + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. + std::tuple get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); } + + // For anything, return an extra point that should also be considered a part of this. + std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } + + bool operator == (const SurfaceFeature& other) const { + if (this->m_type != other.m_type) return false; + switch (this->m_type) + { + case SurfaceFeatureType::Undef: { break; } + case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); } + case SurfaceFeatureType::Edge: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) || + (this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1)); + } + case SurfaceFeatureType::Plane: + case SurfaceFeatureType::Circle: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON); + } + } + + return false; + } + + bool operator != (const SurfaceFeature& other) const { + return !operator == (other); + } + +private: + SurfaceFeatureType m_type{ SurfaceFeatureType::Undef }; + Vec3d m_pt1{ Vec3d::Zero() }; + Vec3d m_pt2{ Vec3d::Zero() }; + std::optional m_pt3; + double m_value{ 0.0 }; +}; + + + +class MeasuringImpl; + + +class Measuring { +public: + // Construct the measurement object on a given its. + explicit Measuring(const indexed_triangle_set& its); + ~Measuring(); + + + // Given a face_idx where the mouse cursor points, return a feature that + // should be highlighted (if any). + std::optional get_feature(size_t face_idx, const Vec3d& point) const; + + // Return total number of planes. + int get_num_of_planes() const; + + // Returns a list of triangle indices for given plane. + const std::vector& get_plane_triangle_indices(int idx) const; + + // Returns the surface features of the plane with the given index + const std::vector& get_plane_features(unsigned int plane_id) const; + + // Returns the mesh used for measuring + const indexed_triangle_set& get_its() const; + +private: + std::unique_ptr priv; +}; + + +struct DistAndPoints { + DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {} + double dist; + Vec3d from; + Vec3d to; +}; + +struct AngleAndEdges { + AngleAndEdges(double angle_, const Vec3d& center_, const std::pair& e1_, const std::pair& e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} + double angle; + Vec3d center; + std::pair e1; + std::pair e2; + double radius; + bool coplanar; + + static const AngleAndEdges Dummy; +}; + +struct MeasurementResult { + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; + std::optional distance_xyz; + + bool has_distance_data() const { + return distance_infinite.has_value() || distance_strict.has_value(); + } + + bool has_any_data() const { + return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); + } +}; + +// Returns distance/angle between two SurfaceFeatures. +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr); + +inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } +inline Vec3d edge_direction(const SurfaceFeature& edge) { + assert(edge.get_type() == SurfaceFeatureType::Edge); + return edge_direction(edge.get_edge()); +} + +inline Vec3d plane_normal(const SurfaceFeature& plane) { + assert(plane.get_type() == SurfaceFeatureType::Plane); + return std::get<1>(plane.get_plane()); +} + +inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } +inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } + +inline bool are_parallel(const std::pair& e1, const std::pair& e2) { + return are_parallel(e1.second - e1.first, e2.second - e2.first); +} +inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_parallel(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_perpendicular(edge_direction(f1), plane_normal(f2)); + else + return false; +} + +inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_perpendicular(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_parallel(edge_direction(f1), plane_normal(f2)); + else + return false; +} + +} // namespace Measure +} // namespace Slic3r + +#endif // Slic3r_Measure_hpp_ diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp new file mode 100644 index 0000000000..8a63de5a1f --- /dev/null +++ b/src/libslic3r/MeasureUtils.hpp @@ -0,0 +1,390 @@ +///|/ Copyright (c) Prusa Research 2022 Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef Slic3r_MeasureUtils_hpp_ +#define Slic3r_MeasureUtils_hpp_ + +#include + +namespace Slic3r { +namespace Measure { + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h + +class Polynomial1 +{ +public: + Polynomial1(std::initializer_list values) + { + // C++ 11 will call the default constructor for + // Polynomial1 p{}, so it is guaranteed that + // values.size() > 0. + m_coefficient.resize(values.size()); + std::copy(values.begin(), values.end(), m_coefficient.begin()); + EliminateLeadingZeros(); + } + + // Construction and destruction. The first constructor creates a + // polynomial of the specified degree but sets all coefficients to + // zero (to ensure initialization). You are responsible for setting + // the coefficients, presumably with the degree-term set to a nonzero + // number. In the second constructor, the degree is the number of + // initializers plus 1, but then adjusted so that coefficient[degree] + // is not zero (unless all initializer values are zero). + explicit Polynomial1(uint32_t degree) + : m_coefficient(static_cast(degree) + 1, 0.0) + {} + + // Eliminate any leading zeros in the polynomial, except in the case + // the degree is 0 and the coefficient is 0. The elimination is + // necessary when arithmetic operations cause a decrease in the degree + // of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) = + // (2 + 3*x). The inputs both have degree 2, so the result is created + // with degree 2. After the addition we find that the degree is in + // fact 1 and resize the array of coefficients. This function is + // called internally by the arithmetic operators, but it is exposed in + // the public interface in case you need it for your own purposes. + void EliminateLeadingZeros() + { + const size_t size = m_coefficient.size(); + if (size > 1) { + const double zero = 0.0; + int32_t leading; + for (leading = static_cast(size) - 1; leading > 0; --leading) { + if (m_coefficient[leading] != zero) + break; + } + + m_coefficient.resize(++leading); + } + } + + // Set all coefficients to the specified value. + void SetCoefficients(double value) + { + std::fill(m_coefficient.begin(), m_coefficient.end(), value); + } + + inline uint32_t GetDegree() const + { + // By design, m_coefficient.size() > 0. + return static_cast(m_coefficient.size() - 1); + } + + inline const double& operator[](uint32_t i) const { return m_coefficient[i]; } + inline double& operator[](uint32_t i) { return m_coefficient[i]; } + + // Evaluate the polynomial. If the polynomial is invalid, the + // function returns zero. + double operator()(double t) const + { + int32_t i = static_cast(m_coefficient.size()); + double result = m_coefficient[--i]; + for (--i; i >= 0; --i) { + result *= t; + result += m_coefficient[i]; + } + return result; + } + +protected: + // The class is designed so that m_coefficient.size() >= 1. + std::vector m_coefficient; +}; + +inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + Polynomial1 result(p0Degree + p1Degree); + result.SetCoefficients(0.0); + for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) { + for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) { + result[i0 + i1] += p0[i0] * p1[i1]; + } + } + return result; +} + +inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = -p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +inline Polynomial1 operator * (double scalar, const Polynomial1& p) +{ + const uint32_t degree = p.GetDegree(); + Polynomial1 result(degree); + for (uint32_t i = 0; i <= degree; ++i) { + result[i] = scalar * p[i]; + } + return result; +} + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h + +class RootsPolynomial +{ +public: + // General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c' + // must have at least d+1 elements and the output array 'root' must + // have at least d elements. + + // Find the roots on (-infinity,+infinity). + static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots) + { + if (degree >= 0 && c != nullptr) { + const double zero = 0.0; + while (degree >= 0 && c[degree] == zero) { + --degree; + } + + if (degree > 0) { + // Compute the Cauchy bound. + const double one = 1.0; + const double invLeading = one / c[degree]; + double maxValue = zero; + for (int32_t i = 0; i < degree; ++i) { + const double value = std::fabs(c[i] * invLeading); + if (value > maxValue) + maxValue = value; + } + const double bound = one + maxValue; + + return FindRecursive(degree, c, -bound, bound, maxIterations, roots); + } + else if (degree == 0) + // The polynomial is a nonzero constant. + return 0; + else { + // The polynomial is identically zero. + roots[0] = zero; + return 1; + } + } + else + // Invalid degree or c. + return 0; + } + + // If you know that p(tmin) * p(tmax) <= 0, then there must be at + // least one root in [tmin, tmax]. Compute it using bisection. + static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root) + { + const double zero = 0.0; + double pmin = Evaluate(degree, c, tmin); + if (pmin == zero) { + root = tmin; + return true; + } + double pmax = Evaluate(degree, c, tmax); + if (pmax == zero) { + root = tmax; + return true; + } + + if (pmin * pmax > zero) + // It is not known whether the interval bounds a root. + return false; + + if (tmin >= tmax) + // Invalid ordering of interval endpoitns. + return false; + + for (uint32_t i = 1; i <= maxIterations; ++i) { + root = 0.5 * (tmin + tmax); + + // This test is designed for 'float' or 'double' when tmin + // and tmax are consecutive floating-point numbers. + if (root == tmin || root == tmax) + break; + + const double p = Evaluate(degree, c, root); + const double product = p * pmin; + if (product < zero) { + tmax = root; + pmax = p; + } + else if (product > zero) { + tmin = root; + pmin = p; + } + else + break; + } + + return true; + } + + // Support for the Find functions. + static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots) + { + // The base of the recursion. + const double zero = 0.0; + double root = zero; + if (degree == 1) { + int32_t numRoots; + if (c[1] != zero) { + root = -c[0] / c[1]; + numRoots = 1; + } + else if (c[0] == zero) { + root = zero; + numRoots = 1; + } + else + numRoots = 0; + + if (numRoots > 0 && tmin <= root && root <= tmax) { + roots[0] = root; + return 1; + } + return 0; + } + + // Find the roots of the derivative polynomial scaled by 1/degree. + // The scaling avoids the factorial growth in the coefficients; + // for example, without the scaling, the high-order term x^d + // becomes (d!)*x through multiple differentiations. With the + // scaling we instead get x. This leads to better numerical + // behavior of the root finder. + const int32_t derivDegree = degree - 1; + std::vector derivCoeff(static_cast(derivDegree) + 1); + std::vector derivRoots(derivDegree); + for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) { + derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree; + } + const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]); + + int32_t numRoots = 0; + if (numDerivRoots > 0) { + // Find root on [tmin,derivRoots[0]]. + if (Find(degree, c, tmin, derivRoots[0], maxIterations, root)) + roots[numRoots++] = root; + + // Find root on [derivRoots[i],derivRoots[i+1]]. + for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) { + if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root)) + roots[numRoots++] = root; + } + + // Find root on [derivRoots[numDerivRoots-1],tmax]. + if (Find(degree, c, derivRoots[static_cast(numDerivRoots) - 1], tmax, maxIterations, root)) + roots[numRoots++] = root; + } + else { + // The polynomial is monotone on [tmin,tmax], so has at most one root. + if (Find(degree, c, tmin, tmax, maxIterations, root)) + roots[numRoots++] = root; + } + return numRoots; + } + + static double Evaluate(int32_t degree, const double* c, double t) + { + int32_t i = degree; + double result = c[i]; + while (--i >= 0) { + result = t * result + c[i]; + } + return result; + } +}; + +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h + +// Construct a single vector orthogonal to the nonzero input vector. If +// the maximum absolute component occurs at index i, then the orthogonal +// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components +// zero. The index addition i+1 is computed modulo N. +inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength) +{ + double cmax = std::fabs(v[0]); + int32_t imax = 0; + for (int32_t i = 1; i < 3; ++i) { + double c = std::fabs(v[i]); + if (c > cmax) { + cmax = c; + imax = i; + } + } + + Vec3d result = Vec3d::Zero(); + int32_t inext = imax + 1; + if (inext == 3) + inext = 0; + + result[imax] = v[inext]; + result[inext] = -v[imax]; + if (unitLength) { + const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; + const double invLength = 1.0 / std::sqrt(sqrDistance); + result[imax] *= invLength; + result[inext] *= invLength; + } + return result; +} + +} // namespace Slic3r +} // namespace Measure + +#endif // Slic3r_MeasureUtils_hpp_ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 90879d49c2..1cd76fc2c5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,3 +1,16 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2021 Boleslaw Ciesielski +///|/ Copyright (c) 2019 John Drake @foxox +///|/ Copyright (c) 2019 Sijmen Schoon +///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard +///|/ +///|/ ported from lib/Slic3r/Model.pm: +///|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966 +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Model.hpp" #include "libslic3r.h" #include "BuildVolume.hpp" @@ -1693,68 +1706,6 @@ bool ModelObject::has_connectors() const return false; } -indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) -{ - indexed_triangle_set connector_mesh; - - int sectorCount {1}; - switch (CutConnectorShape(connector_attributes.shape)) { - case CutConnectorShape::Triangle: - sectorCount = 3; - break; - case CutConnectorShape::Square: - sectorCount = 4; - break; - case CutConnectorShape::Circle: - sectorCount = 360; - break; - case CutConnectorShape::Hexagon: - sectorCount = 6; - break; - default: - break; - } - - if (connector_attributes.style == CutConnectorStyle::Prizm) - connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); - else if (connector_attributes.type == CutConnectorType::Plug) - connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount)); - else - connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); - - return connector_mesh; -} - -void ModelObject::apply_cut_connectors(const std::string &name) -{ - if (cut_connectors.empty()) - return; - - using namespace Geometry; - - size_t connector_id = cut_id.connectors_cnt(); - for (const CutConnector &connector : cut_connectors) { - TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); - // Mesh will be centered when loading. - ModelVolume *new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); - - Transform3d translate_transform = Transform3d::Identity(); - translate_transform.translate(connector.pos); - Transform3d scale_transform = Transform3d::Identity(); - scale_transform.scale(Vec3f(connector.radius, connector.radius, connector.height).cast()); - - // Transform the new modifier to be aligned inside the instance - new_volume->set_transformation(translate_transform * connector.rotation_m * scale_transform); - - new_volume->cut_info = {connector.attribs.type, connector.radius, connector.height, connector.radius_tolerance, connector.height_tolerance}; - new_volume->name = name + "-" + std::to_string(++connector_id); - } - cut_id.increase_connectors_cnt(cut_connectors.size()); - - // delete all connectors - cut_connectors.clear(); -} - void ModelObject::invalidate_cut() { this->cut_id.invalidate(); @@ -1770,43 +1721,10 @@ void ModelObject::delete_connectors() } } -void ModelObject::synchronize_model_after_cut() -{ - for (ModelObject *obj : m_model->objects) { - if (obj == this || obj->cut_id.is_equal(this->cut_id)) continue; - if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id)) - obj->cut_id.copy(this->cut_id); - } -} - -void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) -{ - // we don't save cut information, if result will not contains all parts of initial object - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || - !attributes.has(ModelObjectCutAttribute::KeepLower) || - attributes.has(ModelObjectCutAttribute::InvalidateCutInfo)) - return; - - if (cut_id.id().invalid()) - cut_id.init(); - - { - int cut_obj_cnt = -1; - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - cut_obj_cnt++; - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - cut_obj_cnt++; - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) - cut_obj_cnt++; - if (cut_obj_cnt > 0) - cut_id.increase_check_sum(size_t(cut_obj_cnt)); - } -} - void ModelObject::clone_for_cut(ModelObject **obj) { (*obj) = ModelObject::new_clone(*this); - (*obj)->set_model(nullptr); + (*obj)->set_model(this->get_model()); (*obj)->sla_support_points.clear(); (*obj)->sla_drain_holes.clear(); (*obj)->sla_points_status = sla::PointsStatus::NoPoints; @@ -1814,189 +1732,11 @@ void ModelObject::clone_for_cut(ModelObject **obj) (*obj)->input_file.clear(); } -Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array& plane_points) +void ModelVolume::reset_extra_facets() { - Vec3d mid_point = {0.0, 0.0, 0.0}; - for (auto pt : plane_points) - mid_point += pt; - mid_point /= (double) plane_points.size(); - - Vec3d movement = -mid_point; - - Vec3d v01 = plane_points[1] - plane_points[0]; - Vec3d v12 = plane_points[2] - plane_points[1]; - - Vec3d plane_normal = v01.cross(v12); - plane_normal.normalize(); - - Vec3d axis = {0.0, 0.0, 0.0}; - double phi = 0.0; - Matrix3d matrix; - matrix.setIdentity(); - Geometry::rotation_from_two_vectors(plane_normal, {0.0, 0.0, 1.0}, axis, phi, &matrix); - Vec3d angles = Geometry::extract_euler_angles(matrix); - - movement = matrix * movement; - Transform3d transfo; - transfo.setIdentity(); - transfo.translate(movement); - transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX())); - return transfo; -} - -void ModelObject::process_connector_cut( - ModelVolume *volume, - const Transform3d & instance_matrix, - const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, - ModelObject *upper, ModelObject *lower, - std::vector &dowels, - Vec3d &local_dowels_displace) -{ - assert(volume->cut_info.is_connector); - volume->cut_info.set_processed(); - - const auto volume_matrix = volume->get_matrix(); - - // ! Don't apply instance transformation for the conntectors. - // This transformation is already there - if (volume->cut_info.connector_type != CutConnectorType::Dowel) { - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume *vol = upper->add_volume(*volume); - vol->set_transformation(volume_matrix); - vol->apply_tolerance(); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume *vol = lower->add_volume(*volume); - vol->set_transformation(volume_matrix); - // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug - vol->set_type(ModelVolumeType::MODEL_PART); - } - } - else { - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { - ModelObject *dowel{nullptr}; - // Clone the object to duplicate instances, materials etc. - clone_for_cut(&dowel); - - // add one more solid part same as connector if this connector is a dowel - ModelVolume *vol = dowel->add_volume(*volume); - vol->set_type(ModelVolumeType::MODEL_PART); - - // But discard rotation and Z-offset for this volume - vol->set_rotation(Vec3d::Zero()); - vol->set_offset(Z, 0.0); - - // Compute the displacement (in instance coordinates) to be applied to place the dowels - local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); - - dowels.push_back(dowel); - } - - // Cut the dowel - volume->apply_tolerance(); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh); - - // add small Z offset to better preview - upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); - lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); - - // Add cut parts to the related objects - add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); - add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); - } -} - -void ModelObject::process_modifier_cut( - ModelVolume *volume, - const Transform3d &instance_matrix, - const Transform3d &inverse_cut_matrix, - ModelObjectCutAttributes attributes, - ModelObject *upper, - ModelObject *lower) -{ - const auto volume_matrix = instance_matrix * volume->get_matrix(); - - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::CutToParts)) { - upper->add_volume(*volume); - return; - } - - // Some logic for the negative volumes/connectors. Add only needed modifiers - auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); - bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) - lower->add_volume(*volume); -} - -void ModelObject::process_volume_cut(ModelVolume * volume, - const Transform3d & instance_matrix, - const Transform3d & cut_matrix, - ModelObjectCutAttributes attributes, - TriangleMesh & upper_mesh, - TriangleMesh & lower_mesh) -{ - const auto volume_matrix = volume->get_matrix(); - - using namespace Geometry; - - const Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix); - const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).inverse() - * translation_transform(-1 * cut_transformation.get_offset()); - - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); - - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper_mesh = TriangleMesh(upper_its); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower_mesh = TriangleMesh(lower_its); -} - -void ModelObject::process_solid_part_cut(ModelVolume * volume, - const Transform3d & instance_matrix, - const Transform3d & cut_matrix, - const std::array &plane_points, - ModelObjectCutAttributes attributes, - ModelObject * upper, - ModelObject * lower, - Vec3d & local_displace) -{ - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); - - // Add required cut parts to the objects - if (attributes.has(ModelObjectCutAttribute::CutToParts)) { - add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); - add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); - return; - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - add_cut_volume(upper_mesh, upper, volume, cut_matrix); - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - add_cut_volume(lower_mesh, lower, volume, cut_matrix); - - // Compute the displacement (in instance coordinates) to be applied to place the upper parts - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } + this->supported_facets.reset(); + this->seam_facets.reset(); + this->mmu_segmentation_facets.reset(); } static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance) @@ -2073,215 +1813,6 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan } } -// BBS: replace z with plane_points -ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_points, ModelObjectCutAttributes attributes) -{ - if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // apply cut attributes for object - apply_cut_attributes(attributes); - - ModelObject* upper{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - clone_for_cut(&upper); - - ModelObject* lower{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts)) - clone_for_cut(&lower); - - // Because transformations are going to be applied to meshes directly, - // we reset transformation of all instances and volumes, - // except for translation and Z-rotation on instances, which are preserved - // in the transformation matrix and not applied to the mesh transform. - - // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = Geometry::assemble_transform( - Vec3d::Zero(), // don't apply offset - instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), // BBS: do apply Z-rotation - instances[instance]->get_scaling_factor(), - instances[instance]->get_mirror() - ); - - // BBS - //z -= instances[instance]->get_offset().z(); - for (Vec3d& point : plane_points) { - point -= instances[instance]->get_offset(); - } - Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); - Transform3d cut_matrix = inverse_cut_matrix.inverse(); - - std::vector dowels; - // Displacement (in instance coordinates) to be applied to place the upper parts - Vec3d local_displace = Vec3d::Zero(); - Vec3d local_dowels_displace = Vec3d::Zero(); - - for (ModelVolume *volume : volumes) { - const auto volume_matrix = volume->get_matrix(); - - volume->supported_facets.reset(); - volume->seam_facets.reset(); - volume->mmu_segmentation_facets.reset(); - - if (! volume->is_model_part()) { - if (volume->cut_info.is_processed) { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - //Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); - process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); - } - else { - process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace); - } - } - else if (! volume->mesh().empty()) { - process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace); - } - } - - ModelObjectPtrs res; - - if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) { - reset_instance_transformation(upper, instance, cut_matrix); - res.push_back(upper); - } - else { - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace); - - res.push_back(upper); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); - - res.push_back(lower); - } - - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { - for (auto dowel : dowels) { - reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); - - local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - res.push_back(dowel); - } - } - } - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; - - synchronize_model_after_cut(); - - return res; -} - -// BBS -ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders, double smoothing_alpha, int segment_number) -{ - BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - start"; - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper = ModelObject::new_clone(*this); - - upper->set_model(nullptr); - upper->sla_support_points.clear(); - upper->sla_drain_holes.clear(); - upper->sla_points_status = sla::PointsStatus::NoPoints; - upper->clear_volumes(); - upper->input_file.clear(); - - // Because transformations are going to be applied to meshes directly, - // we reset transformation of all instances and volumes, - // except for translation and Z-rotation on instances, which are preserved - // in the transformation matrix and not applied to the mesh transform. - - // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = Geometry::assemble_transform( - Vec3d::Zero(), // don't apply offset - instances[instance]->get_rotation(), // BBS: keep Z-rotation - instances[instance]->get_scaling_factor(), - instances[instance]->get_mirror() - ); - - for (ModelVolume* volume : volumes) { - const auto volume_matrix = volume->get_matrix(); - - volume->supported_facets.reset(); - volume->seam_facets.reset(); - - if (!volume->is_model_part()) { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - upper->add_volume(*volume); - } - else if (!volume->mesh().empty()) { - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(instance_matrix * volume_matrix, true); - volume->reset_mesh(); - - auto mesh_segments = MeshBoolean::cgal::segment(mesh, smoothing_alpha, segment_number); - - - // Reset volume transformation except for offset - const Vec3d offset = volume->get_offset(); - volume->set_transformation(Geometry::Transformation()); - volume->set_offset(offset); - - unsigned int extruder_counter = 0; - for (int idx=0;idx 0) { - ModelVolume* vol = upper->add_volume(mesh_segment); - vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_" + std::to_string(idx); - // Don't copy the config's ID. - vol->config.assign_config(volume->config); -#if 0 - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); -#else - vol->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter)); -#endif - } - } - } - } - - ModelObjectPtrs res; - - if (upper->volumes.size() > 0) { - upper->invalidate_bounding_box(); - - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); i++) { - auto& instance = upper->instances[i]; - const Vec3d offset = instance->get_offset(); - // BBS - //const double rot_z = instance->get_rotation()(2); - - instance->set_transformation(Geometry::Transformation()); - instance->set_offset(offset); - // BBS - //instance->set_rotation(Vec3d(0.0, 0.0, rot_z)); - } - - res.push_back(upper); - } - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - end"; - - return res; -} - void ModelObject::split(ModelObjectPtrs* new_objects) { std::vector all_meshes; @@ -2758,6 +2289,14 @@ int ModelObject::get_repaired_errors_count(const int vol_idx /*= -1*/) const stats.facets_reversed + stats.backwards_edges; } +bool ModelObject::has_solid_mesh() const +{ + for (const ModelVolume* volume : volumes) + if (volume->is_model_part()) + return true; + return false; +} + void ModelVolume::set_material_id(t_model_material_id material_id) { m_material_id = material_id; @@ -2801,35 +2340,6 @@ bool ModelVolume::is_splittable() const return m_is_splittable == 1; } -void ModelVolume::apply_tolerance() -{ - assert(cut_info.is_connector); - if (!cut_info.is_processed) - return; - - Vec3d sf = get_scaling_factor(); - // make a "hole" wider - double size_scale = 1.f; - if (abs(cut_info.radius - 0) < EPSILON) // For compatibility with old files - size_scale = 1.f + double(cut_info.radius_tolerance); - else - size_scale = (double(cut_info.radius) + double(cut_info.radius_tolerance)) / double(cut_info.radius); - - sf[X] *= size_scale; - sf[Y] *= size_scale; - - // make a "hole" dipper - double height_scale = 1.f; - if (abs(cut_info.height - 0) < EPSILON) // For compatibility with old files - height_scale = 1.f + double(cut_info.height_tolerance); - else - height_scale = (double(cut_info.height) + double(cut_info.height_tolerance)) / double(cut_info.height); - - sf[Z] *= height_scale; - - set_scaling_factor(sf); -} - // BBS std::vector ModelVolume::get_extruders() const { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 481552c058..02220c2aad 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -246,79 +246,92 @@ private: }; enum class CutConnectorType : int { - Plug, - Dowel, - Undef + Plug + , Dowel + , Snap + , Undef }; enum class CutConnectorStyle : int { - Prizm, - Frustum, - Undef + Prism + , Frustum + , Undef //,Claw }; enum class CutConnectorShape : int { - Triangle, - Square, - Hexagon, - Circle, - Undef + Triangle + , Square + , Hexagon + , Circle + , Undef //,D-shape }; struct CutConnectorAttributes { - CutConnectorType type{CutConnectorType::Plug}; - CutConnectorStyle style{CutConnectorStyle::Prizm}; - CutConnectorShape shape{CutConnectorShape::Circle}; + CutConnectorType type{ CutConnectorType::Plug }; + CutConnectorStyle style{ CutConnectorStyle::Prism }; + CutConnectorShape shape{ CutConnectorShape::Circle }; CutConnectorAttributes() {} - CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) : type(t), style(st), shape(sh) {} + CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) + : type(t), style(st), shape(sh) + {} - CutConnectorAttributes(const CutConnectorAttributes &rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} + CutConnectorAttributes(const CutConnectorAttributes& rhs) : + CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} - bool operator==(const CutConnectorAttributes &other) const; + bool operator==(const CutConnectorAttributes& other) const; - bool operator!=(const CutConnectorAttributes &other) const { return !(other == (*this)); } + bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); } - bool operator<(const CutConnectorAttributes &other) const - { - return this->type < other.type || (this->type == other.type && this->style < other.style) || - (this->type == other.type && this->style == other.style && this->shape < other.shape); + bool operator<(const CutConnectorAttributes& other) const { + return this->type < other.type || + (this->type == other.type && this->style < other.style) || + (this->type == other.type && this->style == other.style && this->shape < other.shape); } - template inline void serialize(Archive &ar) { ar(type, style, shape); } + template inline void serialize(Archive& ar) { + ar(type, style, shape); + } }; struct CutConnector { - Vec3d pos; - Transform3d rotation_m; - float radius; - float height; - float radius_tolerance; // [0.f : 1.f] - float height_tolerance; // [0.f : 1.f] + Vec3d pos; + Transform3d rotation_m; + float radius; + float height; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] + float z_angle {0.f}; CutConnectorAttributes attribs; - CutConnector() : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} - - CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) - : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + CutConnector() + : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f), z_angle(0.f) {} - CutConnector(const CutConnector &rhs) : CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, float za, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), z_angle(za), attribs(attributes) + {} - bool operator==(const CutConnector &other) const; + CutConnector(const CutConnector& rhs) : + CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.z_angle, rhs.attribs) {} - bool operator!=(const CutConnector &other) const { return !(other == (*this)); } + bool operator==(const CutConnector& other) const; - template inline void serialize(Archive &ar) { ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); } + bool operator!=(const CutConnector& other) const { return !(other == (*this)); } + + template inline void serialize(Archive& ar) { + ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, z_angle, attribs); + } }; using CutConnectors = std::vector; + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -326,13 +339,9 @@ enum class ModelVolumeType : int { NEGATIVE_VOLUME, PARAMETER_MODIFIER, SUPPORT_BLOCKER, - SUPPORT_ENFORCER + SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, CutToParts, InvalidateCutInfo }; -using ModelObjectCutAttributes = enum_bitmask; -ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); - // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, @@ -371,6 +380,10 @@ public: // Holes to be drilled into the object so resin can flow out sla::DrainHoles sla_drain_holes; + // Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform + CutConnectors cut_connectors; + CutObjectBase cut_id; + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment @@ -380,10 +393,6 @@ public: // BBS: save for compare with new load volumes std::vector volume_ids; - // Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform - CutConnectors cut_connectors; - CutObjectBase cut_id; - Model* get_model() { return m_model; } const Model* get_model() const { return m_model; } // BBS: production extension @@ -480,52 +489,13 @@ public: size_t materials_count() const; size_t facets_count() const; size_t parts_count() const; - - bool is_cut() const { return cut_id.id().valid(); } - bool has_connectors() const; - static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); - void apply_cut_connectors(const std::string &name); // invalidate cut state for this object and its connectors/volumes void invalidate_cut(); // delete volumes which are marked as connector for this object void delete_connectors(); - void synchronize_model_after_cut(); - void apply_cut_attributes(ModelObjectCutAttributes attributes); void clone_for_cut(ModelObject **obj); - Transform3d calculate_cut_plane_inverse_matrix(const std::array &plane_points); - void process_connector_cut(ModelVolume *volume, - const Transform3d & instance_matrix, - const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, - ModelObject *upper, ModelObject *lower, - std::vector &dowels, - Vec3d &local_dowels_displace); - void process_modifier_cut(ModelVolume * volume, - const Transform3d & instance_matrix, - const Transform3d & inverse_cut_matrix, - ModelObjectCutAttributes attributes, - ModelObject * upper, - ModelObject * lower); - void process_volume_cut(ModelVolume * volume, - const Transform3d & instance_matrix, - const Transform3d & cut_matrix, - ModelObjectCutAttributes attributes, - TriangleMesh & upper_mesh, - TriangleMesh & lower_mesh); - void process_solid_part_cut(ModelVolume * volume, - const Transform3d & instance_matrix, - const Transform3d & cut_matrix, - const std::array &plane_points, - ModelObjectCutAttributes attributes, - ModelObject * upper, - ModelObject * lower, - Vec3d & local_displace); - // BBS: replace z with plane_points - ModelObjectPtrs cut(size_t instance, std::array plane_points, ModelObjectCutAttributes attributes); - // BBS - ModelObjectPtrs segment(size_t instance, unsigned int max_extruders, double smoothing_alpha = 0.5, int segment_number = 5); - void split(ModelObjectPtrs* new_objects); + void split(ModelObjectPtrs*new_objects); void merge(); // BBS: Boolean opts - Musang King @@ -553,6 +523,10 @@ public: // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) int get_repaired_errors_count(const int vol_idx = -1) const; + // Detect if object has at least one solid mash + bool has_solid_mesh() const; + bool is_cut() const { return cut_id.id().valid(); } + bool has_connectors() const; private: friend class Model; // This constructor assigns new ID to this ModelObject and its config. @@ -853,37 +827,45 @@ public: }; Source source; - // struct used by cut command + // struct used by cut command // It contains information about connetors struct CutInfo { - bool is_connector{false}; - bool is_processed{true}; - CutConnectorType connector_type{CutConnectorType::Plug}; - float radius{0.f}; - float height{0.f}; - float radius_tolerance{0.f}; // [0.f : 1.f] - float height_tolerance{0.f}; // [0.f : 1.f] + bool is_from_upper{ true }; + bool is_connector{ false }; + bool is_processed{ true }; + CutConnectorType connector_type{ CutConnectorType::Plug }; + float radius_tolerance{ 0.f };// [0.f : 1.f] + float height_tolerance{ 0.f };// [0.f : 1.f] CutInfo() = default; - CutInfo(CutConnectorType type, float radius_, float height_, float rad_tolerance, float h_tolerance, bool processed = false) - : is_connector(true), is_processed(processed), connector_type(type) - , radius(radius_), height(height_), radius_tolerance(rad_tolerance), height_tolerance(h_tolerance) + CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) : + is_connector(true), + is_processed(processed), + connector_type(type), + radius_tolerance(rad_tolerance), + height_tolerance(h_tolerance) {} void set_processed() { is_processed = true; } - void invalidate() { is_connector = false; } + void invalidate() { is_connector = false; } + void reset_from_upper() { is_from_upper = true; } - template inline void serialize(Archive &ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); } + template inline void serialize(Archive& ar) { + ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); + } }; - CutInfo cut_info; + CutInfo cut_info; - bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } - void invalidate_cut_info() { cut_info.invalidate(); } + bool is_from_upper() const { return cut_info.is_from_upper; } + void reset_from_upper() { cut_info.reset_from_upper(); } + + bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } + void invalidate_cut_info() { cut_info.invalidate(); } // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } - const TriangleMesh* mesh_ptr() const { return m_mesh.get(); } + std::shared_ptr mesh_ptr() const { return m_mesh; } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared(mesh); } @@ -922,6 +904,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } t_model_material_id material_id() const { return m_material_id; } + void reset_extra_facets(); void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; void set_material(t_model_material_id material_id, const ModelMaterial &material); @@ -931,8 +914,6 @@ public: bool is_splittable() const; - void apply_tolerance(); - // BBS std::vector get_extruders() const; void update_extruder_count(size_t extruder_count); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 488f836cfc..e25068572d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -899,7 +899,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP offset_top_surface = 0; // don't takes into account too thin areas // skip if the exposed area is smaller than "min_width_top_surface" - double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), config->min_width_top_surface.get_abs_value(perimeter_width)); + double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))); Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface); @@ -1404,7 +1404,7 @@ void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area) } // Reorient loop direction -static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole) +static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole, bool reverse_internal_only) { if (steep_overhang_hole || steep_overhang_contour) { for (auto entity : entities) { @@ -1412,7 +1412,18 @@ static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_ ExtrusionLoop *eloop = static_cast(entity); // Only reverse when needed bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour; - if (need_reverse) { + + bool isExternal = false; + if(reverse_internal_only){ + for(auto path : eloop->paths){ + if(path.role() == erExternalPerimeter){ + isExternal = true; + break; + } + } + } + + if (need_reverse && !isExternal) { eloop->make_clockwise(); } } @@ -1710,7 +1721,7 @@ void PerimeterGenerator::process_classic() bool steep_overhang_contour = false; bool steep_overhang_hole = false; ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole); - reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole); + reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only); // if brim will be printed, reverse the order of perimeters so that // we continue inwards after having finished the brim @@ -2232,7 +2243,7 @@ void PerimeterGenerator::process_arachne() bool steep_overhang_contour = false; bool steep_overhang_hole = false; if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { - reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole); + reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only); this->loops->append(extrusion_coll); } diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 6c6bac8800..73252f4459 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -55,7 +55,7 @@ using Vec2f = Eigen::Matrix; using Vec3f = Eigen::Matrix; using Vec2d = Eigen::Matrix; using Vec3d = Eigen::Matrix; -// BBS +using Vec4f = Eigen::Matrix; using Vec4d = Eigen::Matrix; using Points = std::vector; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c8c158f930..c9777b3c38 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -726,7 +726,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle) static std::vector s_Preset_print_options { "layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode", "top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness", - "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold", + "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern", "infill_direction", "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fa5d7f631b..33cdcab5be 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -20,6 +20,7 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ +#include "Config.hpp" #include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" @@ -2463,7 +2464,7 @@ void Print::_make_wipe_tower() } this->throw_if_canceled(); - if (!m_config.purge_in_prime_tower) { + if (is_BBL_printer()) { // in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume. WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_config.prime_volume, m_wipe_tower_data.tool_ordering.first_extruder(), m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z); @@ -2587,17 +2588,20 @@ void Print::_make_wipe_tower() for (const auto extruder_id : layer_tools.extruders) { if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */ extruder_id != current_extruder_id) { - float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange - volume_to_wipe *= m_config.flush_multiplier; - // Not all of that can be used for infill purging: - volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); + float volume_to_wipe = m_config.prime_volume; + if (m_config.purge_in_prime_tower) { + volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange + volume_to_wipe *= m_config.flush_multiplier; + // Not all of that can be used for infill purging: + volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); - // try to assign some infills/objects for the wiping: - volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, - volume_to_wipe); + // try to assign some infills/objects for the wiping: + volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, + volume_to_wipe); - // add back the minimal amount toforce on the wipe tower: - volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); + // add back the minimal amount toforce on the wipe tower: + volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id); + } // request a toolchange at the wipe tower with at least volume_to_wipe purging amount wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height, @@ -2712,6 +2716,7 @@ DynamicConfig PrintStatistics::config() const config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight)); config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost)); config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament)); + config.set_key_value("initial_tool", new ConfigOptionInt(static_cast(this->initial_tool))); return config; } @@ -2721,7 +2726,7 @@ DynamicConfig PrintStatistics::placeholders() for (const std::string &key : { "print_time", "normal_print_time", "silent_print_time", "used_filament", "extruded_volume", "total_cost", "total_weight", - "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"}) + "initial_tool", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"}) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); return config; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index afa04efce4..eba85dbcfa 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -742,6 +742,7 @@ struct PrintStatistics double total_weight; double total_wipe_tower_cost; double total_wipe_tower_filament; + unsigned int initial_tool; std::map filament_stats; // Config with the filled in print statistics. @@ -759,6 +760,7 @@ struct PrintStatistics total_weight = 0.; total_wipe_tower_cost = 0.; total_wipe_tower_filament = 0.; + initial_tool = 0; filament_stats.clear(); } }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 94cb997fbc..cefc09347c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -840,7 +840,15 @@ void PrintConfigDef::init_fff_params() def->label = L("Reverse on odd"); def->full_label = L("Overhang reversal"); def->category = L("Quality"); - def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhang."); + def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("overhang_reverse_internal_only", coBool); + def->label = L("Reverse only internal perimeters"); + def->full_label = L("Reverse only internal perimeters"); + def->category = L("Quality"); + def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters. \n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recomended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on odd layers irrespective of their overhang degree."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); @@ -1454,7 +1462,6 @@ void PrintConfigDef::init_fff_params() "Can't be zero"); def->sidetext = L("mm³/s"); def->min = 0; - def->max = 200; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloats { 2. }); @@ -2817,7 +2824,7 @@ def = this->add("filament_loading_speed", coFloats); def->tooltip = L("User can self-define the project file name when export"); def->full_width = true; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[0]}_{print_time}.gcode")); + def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode")); def = this->add("make_overhang_printable", coBool); def->label = L("Make overhang printable"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6404b367ae..40cc7a6d62 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -877,6 +877,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold)) ((ConfigOptionBool, hole_to_polyhole_twisted)) ((ConfigOptionBool, overhang_reverse)) + ((ConfigOptionBool, overhang_reverse_internal_only)) ((ConfigOptionFloatOrPercent, overhang_reverse_threshold)) ) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8d6175cc0a..48da72f5f3 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1097,6 +1097,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "fuzzy_skin_point_distance" || opt_key == "detect_overhang_wall" || opt_key == "overhang_reverse" + || opt_key == "overhang_reverse_internal_only" || opt_key == "overhang_reverse_threshold" //BBS || opt_key == "enable_overhang_speed" diff --git a/src/libslic3r/Semver.hpp b/src/libslic3r/Semver.hpp index cdc96f108b..4d64b1c7db 100644 --- a/src/libslic3r/Semver.hpp +++ b/src/libslic3r/Semver.hpp @@ -110,10 +110,30 @@ public: void set_maj(int maj) { ver.major = maj; } void set_min(int min) { ver.minor = min; } void set_patch(int patch) { ver.patch = patch; } - void set_metadata(boost::optional meta) { ver.metadata = meta ? strdup(*meta) : nullptr; } - void set_metadata(const char *meta) { ver.metadata = meta ? strdup(meta) : nullptr; } - void set_prerelease(boost::optional pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; } - void set_prerelease(const char *pre) { ver.prerelease = pre ? strdup(pre) : nullptr; } + void set_metadata(boost::optional meta) + { + if (ver.metadata) + free(ver.metadata); + ver.metadata = meta ? strdup(*meta) : nullptr; + } + void set_metadata(const char *meta) + { + if (ver.metadata) + free(ver.metadata); + ver.metadata = meta ? strdup(meta) : nullptr; + } + void set_prerelease(boost::optional pre) + { + if (ver.prerelease) + free(ver.prerelease); + ver.prerelease = pre ? strdup(*pre) : nullptr; + } + void set_prerelease(const char *pre) + { + if (ver.prerelease) + free(ver.prerelease); + ver.prerelease = pre ? strdup(pre) : nullptr; + } // Comparison bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; } diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp new file mode 100644 index 0000000000..976387c21f --- /dev/null +++ b/src/libslic3r/SurfaceMesh.hpp @@ -0,0 +1,167 @@ +///|/ Copyright (c) Prusa Research 2022 Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_SurfaceMesh_hpp_ +#define slic3r_SurfaceMesh_hpp_ + +#include +#include + +#include "boost/container/small_vector.hpp" + +namespace Slic3r { + +class TriangleMesh; + + + +enum Face_index : int; + +class Halfedge_index { + friend class SurfaceMesh; + +public: + Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} + Face_index face() const { return m_face; } + unsigned char side() const { return m_side; } + bool is_invalid() const { return int(m_face) < 0; } + bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } + bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } + +private: + Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {} + + Face_index m_face; + unsigned char m_side; +}; + + + +class Vertex_index { + friend class SurfaceMesh; + +public: + Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {} + bool is_invalid() const { return int(m_face) < 0; } + bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex. + +private: + Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {} + + Face_index m_face; + unsigned char m_vertex_idx; +}; + + + +class SurfaceMesh { +public: + explicit SurfaceMesh(const indexed_triangle_set& its) + : m_its(its), + m_face_neighbors(its_face_neighbors_par(its)) + {} + SurfaceMesh(const SurfaceMesh&) = delete; + SurfaceMesh& operator=(const SurfaceMesh&) = delete; + + Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); } + Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); } + Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; } + + Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; } + Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; } + Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); } + Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); } + Halfedge_index opposite(Halfedge_index h) const { + if (h.is_invalid()) + return h; + + int face_idx = m_face_neighbors[h.m_face][h.m_side]; + Halfedge_index h_candidate = halfedge(Face_index(face_idx)); + + if (h_candidate.is_invalid()) + return Halfedge_index(); // invalid + + for (int i=0; i<3; ++i) { + if (is_same_vertex(source(h_candidate), target(h))) { + // Meshes in PrusaSlicer should be fixed enough for the following not to happen. + assert(is_same_vertex(target(h_candidate), source(h))); + return h_candidate; + } + h_candidate = next(h_candidate); + } + return Halfedge_index(); // invalid + } + + Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); } + Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); } + Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); } + Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); } + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const + { + Halfedge_index hi(source.m_face, source.m_vertex_idx); + assert(! hi.is_invalid()); + + const Vertex_index orig_target = this->target(hi); + Vertex_index current_target = orig_target; + + while (! is_same_vertex(current_target, target)) { + hi = next_around_source(hi); + if (hi.is_invalid()) + break; + current_target = this->target(hi); + if (is_same_vertex(current_target, orig_target)) + return Halfedge_index(); // invalid + } + + return hi; + } + + const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; } + + size_t degree(Vertex_index v) const + { + // In case the mesh is broken badly, the loop might end up to be infinite, + // never getting back to the first halfedge. Remember list of all half-edges + // and trip if any is encountered for the second time. + Halfedge_index h_first = halfedge(v); + boost::container::small_vector he_visited; + Halfedge_index h = next_around_target(h_first); + size_t degree = 2; + while (! h.is_invalid() && h != h_first) { + he_visited.emplace_back(h); + h = next_around_target(h); + if (std::find(he_visited.begin(), he_visited.end(), h) == he_visited.end()) + return 0; + ++degree; + } + return h.is_invalid() ? 0 : degree - 1; + } + + size_t degree(Face_index f) const { + size_t total = 0; + for (unsigned char i=0; i<3; ++i) { + size_t d = degree(Vertex_index(f, i)); + if (d == 0) + return 0; + total += d; + } + assert(total - 6 >= 0); + return total - 6; // we counted 3 halfedges from f, and one more for each neighbor + } + + bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } + + bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; } + + + +private: + const std::vector m_face_neighbors; + const indexed_triangle_set& m_its; +}; + +} //namespace Slic3r + +#endif // slic3r_SurfaceMesh_hpp_ diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index aa014a6dc9..d8dc5a7d42 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -12,8 +12,6 @@ #define ENABLE_RENDER_SELECTION_CENTER 0 // Shows an imgui dialog with camera related data #define ENABLE_CAMERA_STATISTICS 0 -// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering) -#define ENABLE_RENDER_PICKING_PASS 0 // Enable extracting thumbnails from selected gcode and save them as png files #define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0 // Disable synchronization of unselected instances @@ -61,6 +59,8 @@ #define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2) // Enable fit print volume command for circular printbeds #define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2) +// Enable picking using raytracing +#define ENABLE_RAYCAST_PICKING_DEBUG 0 #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index b5d0d10883..92557dfdb5 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1,3 +1,18 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts +///|/ Copyright (c) 2019 Sijmen Schoon +///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard +///|/ Copyright (c) 2014 Miro Hrončok @hroncok +///|/ Copyright (c) 2014 Petr Ledvina @ledvinap +///|/ +///|/ ported from lib/Slic3r/TriangleMesh.pm: +///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2012 - 2013 Mark Hindess +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Exception.hpp" #include "TriangleMesh.hpp" #include "TriangleMeshSlicer.hpp" @@ -958,6 +973,51 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa) return mesh; } +indexed_triangle_set its_make_frustum(double r, double h, double fa) +{ + indexed_triangle_set mesh; + size_t n_steps = (size_t)ceil(2. * PI / fa); + double angle_step = 2. * PI / n_steps; + + auto &vertices = mesh.vertices; + auto &facets = mesh.indices; + vertices.reserve(2 * n_steps + 2); + facets.reserve(4 * n_steps); + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.emplace_back(Vec3f(0.f, 0.f, 0.f)); + vertices.emplace_back(Vec3f(0.f, 0.f, float(h))); + + // for each line along the polygon approximating the top/bottom of the + // circle, generate four points and four facets (2 for the wall, 2 for the + // top and bottom. + // Special case: Last line shares 2 vertices with the first line. + Vec2f vec_top = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, 0.5f*r); + Vec2f vec_botton = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, r); + + vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f)); + vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h))); + for (size_t i = 1; i < n_steps; ++i) { + vec_top = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, 0.5f*float(r)); + vec_botton = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, float(r)); + vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f)); + vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h))); + int id = (int)vertices.size() - 1; + facets.emplace_back( 0, id - 1, id - 3); // top + facets.emplace_back(id, 1, id - 2); // bottom + facets.emplace_back(id, id - 2, id - 3); // upper-right of side + facets.emplace_back(id, id - 3, id - 1); // bottom-left of side + } + // Connect the last set of vertices with the first. + int id = (int)vertices.size() - 1; + facets.emplace_back( 0, 2, id - 1); + facets.emplace_back( 3, 1, id); + facets.emplace_back(id, 2, 3); + facets.emplace_back(id, id - 1, 2); + + return mesh; +} + indexed_triangle_set its_make_cone(double r, double h, double fa) { indexed_triangle_set mesh; @@ -984,61 +1044,6 @@ indexed_triangle_set its_make_cone(double r, double h, double fa) return mesh; } -// Generates mesh for a frustum dowel centered about the origin, using the count of sectors -// Note: This function uses code for sphere generation, but for stackCount = 2; -indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount) -{ - int stackCount = 2; - float sectorStep = float(2. * M_PI / sectorCount); - float stackStep = float(M_PI / stackCount); - - indexed_triangle_set mesh; - auto& vertices = mesh.vertices; - vertices.reserve((stackCount - 1) * sectorCount + 2); - for (int i = 0; i <= stackCount; ++i) { - // from pi/2 to -pi/2 - double stackAngle = 0.5 * M_PI - stackStep * i; - double xy = radius * cos(stackAngle); - double z = radius * sin(stackAngle); - if (i == 0 || i == stackCount) - vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle)))); - else - for (int j = 0; j < sectorCount; ++j) { - // from 0 to 2pi - double sectorAngle = sectorStep * j + 0.25 * M_PI; - vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast()); - } - } - - auto& facets = mesh.indices; - facets.reserve(2 * (stackCount - 1) * sectorCount); - for (int i = 0; i < stackCount; ++i) { - // Beginning of current stack. - int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); - int k1_first = k1; - // Beginning of next stack. - int k2 = (i == 0) ? 1 : (k1 + sectorCount); - int k2_first = k2; - for (int j = 0; j < sectorCount; ++j) { - // 2 triangles per sector excluding first and last stacks - int k1_next = k1; - int k2_next = k2; - if (i != 0) { - k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); - facets.emplace_back(k1, k2, k1_next); - } - if (i + 1 != stackCount) { - k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); - facets.emplace_back(k1_next, k2, k2_next); - } - k1 = k1_next; - k2 = k2_next; - } - } - - return mesh; -} - indexed_triangle_set its_make_pyramid(float base, float height) { float a = base / 2.f; @@ -1116,6 +1121,182 @@ indexed_triangle_set its_make_sphere(double radius, double fa) return mesh; } +// Generates mesh for a frustum dowel centered about the origin, using the count of sectors +// Note: This function uses code for sphere generation, but for stackCount = 2; +indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount) +{ + int stackCount = 2; + float sectorStep = float(2. * M_PI / sectorCount); + float stackStep = float(M_PI / stackCount); + + indexed_triangle_set mesh; + auto& vertices = mesh.vertices; + vertices.reserve((stackCount - 1) * sectorCount + 2); + for (int i = 0; i <= stackCount; ++i) { + // from pi/2 to -pi/2 + double stackAngle = 0.5 * M_PI - stackStep * i; + double xy = radius * cos(stackAngle); + double z = radius * sin(stackAngle); + if (i == 0 || i == stackCount) + vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle)))); + else + for (int j = 0; j < sectorCount; ++j) { + // from 0 to 2pi + double sectorAngle = sectorStep * j + 0.25 * M_PI; + vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast()); + } + } + + auto& facets = mesh.indices; + facets.reserve(2 * (stackCount - 1) * sectorCount); + for (int i = 0; i < stackCount; ++i) { + // Beginning of current stack. + int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); + int k1_first = k1; + // Beginning of next stack. + int k2 = (i == 0) ? 1 : (k1 + sectorCount); + int k2_first = k2; + for (int j = 0; j < sectorCount; ++j) { + // 2 triangles per sector excluding first and last stacks + int k1_next = k1; + int k2_next = k2; + if (i != 0) { + k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); + facets.emplace_back(k1, k2, k1_next); + } + if (i + 1 != stackCount) { + k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); + facets.emplace_back(k1_next, k2, k2_next); + } + k1 = k1_next; + k2 = k2_next; + } + } + + return mesh; +} + +indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion) +{ + const float radius = (float)r; + const float height = (float)h; + const size_t sectors_cnt = 10; //(float)fa; + const float halfPI = 0.5f * (float)PI; + + const float space_len = space_proportion * radius; + + const float b_len = radius; + const float m_len = (1 + bulge_proportion) * radius; + const float t_len = 0.5f * radius; + + const float b_height = 0.f; + const float m_height = 0.5f * height; + const float t_height = height; + + const float b_angle = acos(space_len/b_len); + const float t_angle = acos(space_len/t_len); + + const float b_angle_step = b_angle / (float)sectors_cnt; + const float t_angle_step = t_angle / (float)sectors_cnt; + + const Vec2f b_vec = Eigen::Vector2f(0, b_len); + const Vec2f t_vec = Eigen::Vector2f(0, t_len); + + + auto add_side_vertices = [b_vec, t_vec, b_height, m_height, t_height](std::vector& vertices, float b_angle, float t_angle, const Vec2f& m_vec) { + Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec; + Vec2f m_pt = Eigen::Rotation2Df(b_angle) * m_vec; + Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec; + + vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); + vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); + vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); + }; + + auto add_side_facets = [](std::vector& facets, int vertices_cnt, int frst_id, int scnd_id) { + int id = vertices_cnt - 1; + + facets.emplace_back(frst_id, id - 2, id - 5); + + facets.emplace_back(id - 2, id - 1, id - 5); + facets.emplace_back(id - 1, id - 4, id - 5); + facets.emplace_back(id - 4, id - 1, id); + facets.emplace_back(id, id - 3, id - 4); + + facets.emplace_back(id, scnd_id, id - 3); + }; + + const float f = (b_len - m_len) / m_len; // Flattening + + auto get_m_len = [b_len, f](float angle) { + const float rad_sqr = b_len * b_len; + const float sin_sqr = sin(angle) * sin(angle); + const float f_sqr = (1-f)*(1-f); + return sqrtf(rad_sqr / (1 + (1 / f_sqr - 1) * sin_sqr)); + }; + + auto add_sub_mesh = [add_side_vertices, add_side_facets, get_m_len, + b_height, t_height, b_angle, t_angle, b_angle_step, t_angle_step] + (indexed_triangle_set& mesh, float center_x, float angle_rotation, int frst_vertex_id) { + auto& vertices = mesh.vertices; + auto& facets = mesh.indices; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.emplace_back(Vec3f(center_x, 0.f, b_height)); + vertices.emplace_back(Vec3f(center_x, 0.f, t_height)); + + float b_angle_start = angle_rotation - b_angle; + float t_angle_start = angle_rotation - t_angle; + const float b_angle_stop = angle_rotation + b_angle; + + const int frst_id = frst_vertex_id; + const int scnd_id = frst_id + 1; + + // add first side vertices and internal facets + { + const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start)); + add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, id - 2, id - 1); + facets.emplace_back(frst_id, id - 1, id); + facets.emplace_back(frst_id, id, scnd_id); + } + + // add d side vertices and facets + while (!is_approx(b_angle_start, b_angle_stop)) { + b_angle_start += b_angle_step; + t_angle_start += t_angle_step; + + const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start)); + add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec); + + add_side_facets(facets, (int)vertices.size(), frst_id, scnd_id); + } + + // add last internal facets to close the mesh + { + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, scnd_id, id); + facets.emplace_back(frst_id, id, id - 1); + facets.emplace_back(frst_id, id - 1, id - 2); + } + }; + + + indexed_triangle_set mesh; + + mesh.vertices.reserve(2 * (3 * (2 * sectors_cnt + 1) + 2)); + mesh.indices.reserve(2 * (6 * 2 * sectors_cnt + 6)); + + add_sub_mesh(mesh, -space_len, halfPI , 0); + add_sub_mesh(mesh, space_len, 3 * halfPI, (int)mesh.vertices.size()); + + return mesh; +} + indexed_triangle_set its_convex_hull(const std::vector &pts) { std::vector dst_vertices; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 9afaddd4eb..12002cc535 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -1,3 +1,14 @@ +///|/ Copyright (c) Prusa Research 2017 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Filip Sykala @Jony01 +///|/ Copyright (c) 2019 Sijmen Schoon +///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/TriangleMesh.pm: +///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2012 - 2013 Mark Hindess +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_TriangleMesh_hpp_ #define slic3r_TriangleMesh_hpp_ @@ -337,9 +348,11 @@ indexed_triangle_set its_make_cube(double x, double y, double z); indexed_triangle_set its_make_prism(float width, float length, float height); indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360)); +indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); +indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f); indexed_triangle_set its_convex_hull(const std::vector &pts); inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 3fd0da6604..204b267c22 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Lukáš Hejl @hejllukas +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "Tesselate.hpp" @@ -2300,79 +2304,4 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u } } -// BBS: implement plane cut with cgal -static Vec3d calc_plane_normal(const std::array& plane_points) -{ - Vec3d v01 = plane_points[1] - plane_points[0]; - Vec3d v12 = plane_points[2] - plane_points[1]; - - Vec3d plane_normal = v01.cross(v12); - plane_normal.normalize(); - return plane_normal; -} - -void cut_mesh -( - const indexed_triangle_set& mesh, // model object coordinate - std::array plane_points, // model object coordinate - indexed_triangle_set* upper, - indexed_triangle_set* lower, - bool triangulate_caps -) -{ - assert(upper || lower); - if (upper == nullptr && lower == nullptr) - return; - - BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object"; - - Vec3d plane_normal = calc_plane_normal(plane_points); - if (std::abs(plane_normal(0)) < EPSILON && std::abs(plane_normal(1)) < EPSILON) { - cut_mesh(mesh, plane_points[0](2), upper, lower); - return; - } - - // BBS - if (std::abs(plane_normal(2)) < EPSILON) { - // keep the side on the normal direction - } - else if (plane_normal(2) < 0.0) { - std::reverse(plane_points.begin(), plane_points.end()); - } - plane_normal = calc_plane_normal(plane_points); - - Vec3d mid_point = { 0.0, 0.0, 0.0 }; - for (auto pt : plane_points) - mid_point += pt; - mid_point /= (double)plane_points.size(); - Vec3d movement = -mid_point; - - Vec3d axis = { 0.0, 0.0, 0.0 }; - double phi = 0.0; - Matrix3d matrix; - matrix.setIdentity(); - Geometry::rotation_from_two_vectors(plane_normal, { 0.0, 0.0, 1.0 }, axis, phi, &matrix); - Vec3d angles = Geometry::extract_euler_angles(matrix); - - movement = matrix * movement; - Transform3d transfo; - transfo.setIdentity(); - transfo.translate(movement); - transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX())); - - indexed_triangle_set mesh_temp = mesh; - its_transform(mesh_temp, transfo); - cut_mesh(mesh_temp, 0., upper, lower); - - Transform3d transfo_inv = transfo.inverse(); - if (upper) { - its_transform(*upper, transfo_inv); - } - - if (lower) { - its_transform(*lower, transfo_inv); - } -} - - } // namespace Slic3r diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 1a8b643629..a7ad62acd9 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2022 Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_TriangleMeshSlicer_hpp_ #define slic3r_TriangleMeshSlicer_hpp_ @@ -130,14 +134,6 @@ void cut_mesh( indexed_triangle_set *lower, bool triangulate_caps = true); -// BBS -void cut_mesh( - const indexed_triangle_set &mesh, - std::array plane_points, - indexed_triangle_set *upper, - indexed_triangle_set *lower, - bool triangulate_caps = true); - -} +} // namespace Slic3r #endif // slic3r_TriangleMeshSlicer_hpp_ diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 08f10b2887..ce12a33486 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -160,6 +160,11 @@ void flush_logs(); // This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded. typedef std::string local_encoded_string; +// Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character. +// Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence. +extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0); +extern size_t get_utf8_sequence_length(const char *seq, size_t size); + // Convert an UTF-8 encoded string into local coding. // On Windows, the UTF-8 string is converted to a local 8-bit code page. // On OSX and Linux, this function does no conversion and returns a copy of the source string. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index f5915175c7..e31656cda9 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -1,3 +1,9 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, David Kocík @kocikdav, Roman Beránek @zavorka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2021 Justin Schuh @jschuh +///|/ Copyright (c) Slic3r 2013 - 2015 Alessandro Ranellucci @alranel +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Utils.hpp" #include "I18N.hpp" @@ -1030,6 +1036,76 @@ bool is_shapes_dir(const std::string& dir) namespace Slic3r { +size_t get_utf8_sequence_length(const std::string& text, size_t pos) +{ + assert(pos < text.size()); + return get_utf8_sequence_length(text.c_str() + pos, text.size() - pos); +} + +size_t get_utf8_sequence_length(const char *seq, size_t size) +{ + size_t length = 0; + unsigned char c = seq[0]; + if (c < 0x80) { // 0x00-0x7F + // is ASCII letter + length++; + } + // Bytes 0x80 to 0xBD are trailer bytes in a multibyte sequence. + // pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes. + else if (c < 0xC0) { // 0x80-0xBF + length++; + while (length < size) { + c = seq[length]; + if (c < 0x80 || c >= 0xC0) { + break; // prevent overrun + } + length++; // add a utf-8 trailer byte + } + } + // Bytes 0xC0 to 0xFD are header bytes in a multibyte sequence. + // The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence. + else if (c < 0xE0) { // 0xC0-0xDF + // add a utf-8 sequence (2 bytes) + if (2 > size) { + return size; // prevent overrun + } + length += 2; + } + else if (c < 0xF0) { // 0xE0-0xEF + // add a utf-8 sequence (3 bytes) + if (3 > size) { + return size; // prevent overrun + } + length += 3; + } + else if (c < 0xF8) { // 0xF0-0xF7 + // add a utf-8 sequence (4 bytes) + if (4 > size) { + return size; // prevent overrun + } + length += 4; + } + else if (c < 0xFC) { // 0xF8-0xFB + // add a utf-8 sequence (5 bytes) + if (5 > size) { + return size; // prevent overrun + } + length += 5; + } + else if (c < 0xFE) { // 0xFC-0xFD + // add a utf-8 sequence (6 bytes) + if (6 > size) { + return size; // prevent overrun + } + length += 6; + } + else { // 0xFE-0xFF + // not a utf-8 sequence + length++; + } + return length; +} + // Encode an UTF-8 string to the local code page. std::string encode_path(const char *src) { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f787937793..2b404c0c30 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -1,3 +1,12 @@ +#/|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +#/|/ Copyright (c) 2023 Pedro Lamas @PedroLamas +#/|/ Copyright (c) 2020 Sergey Kovalev @RandoMan70 +#/|/ Copyright (c) 2021 Boleslaw Ciesielski +#/|/ Copyright (c) 2019 Spencer Owen @spuder +#/|/ Copyright (c) 2019 Stephan Reichhelm @stephanr +#/|/ +#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +#/|/ cmake_minimum_required(VERSION 3.13) project(libslic3r_gui) @@ -99,6 +108,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLShader.hpp GUI/GLCanvas3D.hpp GUI/GLCanvas3D.cpp + GUI/SceneRaycaster.hpp + GUI/SceneRaycaster.cpp GUI/OpenGLManager.hpp GUI/OpenGLManager.cpp GUI/Selection.hpp @@ -115,24 +126,26 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoRotate.hpp GUI/Gizmos/GLGizmoScale.cpp GUI/Gizmos/GLGizmoScale.hpp - GUI/Gizmos/GLGizmoSlaSupports.cpp - GUI/Gizmos/GLGizmoSlaSupports.hpp + #GUI/Gizmos/GLGizmoSlaSupports.cpp + #GUI/Gizmos/GLGizmoSlaSupports.hpp GUI/Gizmos/GLGizmoFdmSupports.cpp GUI/Gizmos/GLGizmoFdmSupports.hpp GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.hpp - GUI/Gizmos/GLGizmoAdvancedCut.cpp - GUI/Gizmos/GLGizmoAdvancedCut.hpp - GUI/Gizmos/GLGizmoHollow.cpp - GUI/Gizmos/GLGizmoHollow.hpp + GUI/Gizmos/GLGizmoCut.cpp + GUI/Gizmos/GLGizmoCut.hpp + #GUI/Gizmos/GLGizmoHollow.cpp + #GUI/Gizmos/GLGizmoHollow.hpp GUI/Gizmos/GLGizmoPainterBase.cpp GUI/Gizmos/GLGizmoPainterBase.hpp GUI/Gizmos/GLGizmoSimplify.cpp GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp - GUI/Gizmos/GLGizmoFaceDetector.cpp - GUI/Gizmos/GLGizmoFaceDetector.hpp + #GUI/Gizmos/GLGizmoFaceDetector.cpp + #GUI/Gizmos/GLGizmoFaceDetector.hpp + GUI/Gizmos/GLGizmoMeasure.cpp + GUI/Gizmos/GLGizmoMeasure.hpp GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.hpp GUI/Gizmos/GLGizmoText.cpp @@ -173,6 +186,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_App.hpp GUI/GUI_Utils.cpp GUI/GUI_Utils.hpp + GUI/GUI_Geometry.cpp + GUI/GUI_Geometry.hpp GUI/I18N.cpp GUI/I18N.hpp GUI/MainFrame.cpp @@ -348,7 +363,6 @@ set(SLIC3R_GUI_SOURCES GUI/Mouse3DController.hpp GUI/IMSlider.cpp GUI/IMSlider.hpp - GUI/IMSlider_Utils.hpp GUI/Notebook.cpp GUI/Notebook.hpp GUI/TabButton.cpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index a7b6e050eb..88c61ac8b1 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -12,6 +12,8 @@ #include "GUI_App.hpp" #include "GUI_Colors.hpp" #include "GLCanvas3D.hpp" +#include "Plater.hpp" +#include "Camera.hpp" #include @@ -26,13 +28,58 @@ #endif static const float GROUND_Z = -0.04f; -static const std::array DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f }; -static const std::array DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f }; -static const std::array PICKING_MODEL_COLOR = { 0.0f, 0.0f, 0.0f, 1.0f }; +static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f }; +static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f }; +static const Slic3r::ColorRGBA DEFAULT_SOLID_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 1.0f }; +static const Slic3r::ColorRGBA DEFAULT_TRANSPARENT_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 0.6f }; namespace Slic3r { namespace GUI { +bool init_model_from_poly(GLModel &model, const ExPolygon &poly, float z) +{ + if (poly.empty()) + return false; + + const std::vector triangles = triangulate_expolygon_2f(poly, NORMALS_UP); + if (triangles.empty() || triangles.size() % 3 != 0) + return false; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2 }; + init_data.reserve_vertices(triangles.size()); + init_data.reserve_indices(triangles.size() / 3); + + Vec2f min = triangles.front(); + Vec2f max = min; + for (const Vec2f &v : triangles) { + min = min.cwiseMin(v).eval(); + max = max.cwiseMax(v).eval(); + } + + const Vec2f size = max - min; + if (size.x() <= 0.0f || size.y() <= 0.0f) + return false; + + Vec2f inv_size = size.cwiseInverse(); + inv_size.y() *= -1.0f; + + // vertices + indices + unsigned int vertices_counter = 0; + for (const Vec2f &v : triangles) { + const Vec3f p = {v.x(), v.y(), z}; + init_data.add_vertex(p, (Vec2f)(v - min).cwiseProduct(inv_size).eval()); + ++vertices_counter; + if (vertices_counter % 3 == 0) + init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); + } + + model.init_from(std::move(init_data)); + + return true; +} + +/* bool GeometryBuffer::set_from_triangles(const std::vector &triangles, float z) { if (triangles.empty()) { @@ -131,41 +178,45 @@ const float* GeometryBuffer::get_vertices_data() const { return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr; } +*/ const float Bed3D::Axes::DefaultStemRadius = 0.5f; const float Bed3D::Axes::DefaultStemLength = 25.0f; const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius; const float Bed3D::Axes::DefaultTipLength = 5.0f; -std::array Bed3D::AXIS_X_COLOR = decode_color_to_float_array("#FF0000"); -std::array Bed3D::AXIS_Y_COLOR = decode_color_to_float_array("#00FF00"); -std::array Bed3D::AXIS_Z_COLOR = decode_color_to_float_array("#0000FF"); +ColorRGBA Bed3D::AXIS_X_COLOR = ColorRGBA::X(); +ColorRGBA Bed3D::AXIS_Y_COLOR = ColorRGBA::Y(); +ColorRGBA Bed3D::AXIS_Z_COLOR = ColorRGBA::Z(); void Bed3D::update_render_colors() { - Bed3D::AXIS_X_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_X]); - Bed3D::AXIS_Y_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Y]); - Bed3D::AXIS_Z_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Z]); + Bed3D::AXIS_X_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_X]); + Bed3D::AXIS_Y_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_Y]); + Bed3D::AXIS_Z_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_Z]); } void Bed3D::load_render_colors() { - RenderColor::colors[RenderCol_Axis_X] = IMColor(Bed3D::AXIS_X_COLOR); - RenderColor::colors[RenderCol_Axis_Y] = IMColor(Bed3D::AXIS_Y_COLOR); - RenderColor::colors[RenderCol_Axis_Z] = IMColor(Bed3D::AXIS_Z_COLOR); + RenderColor::colors[RenderCol_Axis_X] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_X_COLOR); + RenderColor::colors[RenderCol_Axis_Y] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_Y_COLOR); + RenderColor::colors[RenderCol_Axis_Z] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_Z_COLOR); } -void Bed3D::Axes::render() const +void Bed3D::Axes::render() { - auto render_axis = [this](const Transform3f& transform) { - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixf(transform.data())); + auto render_axis = [this](GLShaderProgram* shader, const Transform3d& transform) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * transform); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * transform.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_arrow.render(); - glsafe(::glPopMatrix()); }; if (!m_arrow.is_initialized()) - const_cast(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); + m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -177,16 +228,16 @@ void Bed3D::Axes::render() const shader->set_uniform("emission_factor", 0.0f); // x axis - const_cast(&m_arrow)->set_color(-1, AXIS_X_COLOR); - render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast()); + m_arrow.set_color(AXIS_X_COLOR); + render_axis(shader, Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 })); // y axis - const_cast(&m_arrow)->set_color(-1, AXIS_Y_COLOR); - render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast()); + m_arrow.set_color(AXIS_Y_COLOR); + render_axis(shader, Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 })); // z axis - const_cast(&m_arrow)->set_color(-1, AXIS_Z_COLOR); - render_axis(Geometry::assemble_transform(m_origin).cast()); + m_arrow.set_color(AXIS_Z_COLOR); + render_axis(shader, Geometry::assemble_transform(m_origin)); shader->stop_using(); @@ -259,25 +310,9 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig //BBS: add part plate logic //BBS add default bed -#if 1 - ExPolygon poly{ Polygon::new_scale(printable_area) }; -#else - ExPolygon poly; - for (const Vec2d& p : printable_area) { - poly.contour.append(Point(scale_(p(0) + m_position.x()), scale_(p(1) + m_position.y()))); - } -#endif - - calc_triangles(poly); - - //no need gridline for 3dbed - //const BoundingBox& bed_bbox = poly.contour.bounding_box(); - //calc_gridlines(poly, bed_bbox); - - //m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0]; + m_triangles.reset(); if (with_reset) { - this->release_VBOs(); //m_texture.reset(); m_model.reset(); } @@ -290,6 +325,10 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig m_axes.set_origin({ 0.0, 0.0, static_cast(GROUND_Z) }); m_axes.set_stem_length(0.1f * static_cast(m_build_volume.bounding_volume().max_size())); + // unregister from picking + // BBS: remove the bed picking logic + // wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed); + // Let the calee to update the UI. return true; } @@ -325,33 +364,28 @@ void Bed3D::on_change_color_mode(bool is_dark) m_is_dark = is_dark; } -void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) +void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes) { - render_internal(canvas, bottom, scale_factor, show_axes); + render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes); } -/*void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor) -{ - render_internal(canvas, bottom, scale_factor, false, false, true); -}*/ - -void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, +void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes) { - float* factor = const_cast(&m_scale_factor); - *factor = scale_factor; + m_scale_factor = scale_factor; if (show_axes) render_axes(); + glsafe(::glEnable(GL_DEPTH_TEST)); - m_model.set_color(-1, m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR); + m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR); switch (m_type) { - case Type::System: { render_system(canvas, bottom); break; } + case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom); break; } default: - case Type::Custom: { render_custom(canvas, bottom); break; } + case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom); break; } } glsafe(::glDisable(GL_DEPTH_TEST)); @@ -388,39 +422,6 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box(bool consider_model_offset) cons return out; } -void Bed3D::calc_triangles(const ExPolygon& poly) -{ - if (! m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) - BOOST_LOG_TRIVIAL(error) << "Unable to create bed triangles"; -} - -void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) -{ - /*Polylines axes_lines; - for (coord_t x = bed_bbox.min.x(); x <= bed_bbox.max.x(); x += scale_(10.0)) { - Polyline line; - line.append(Point(x, bed_bbox.min.y())); - line.append(Point(x, bed_bbox.max.y())); - axes_lines.push_back(line); - } - for (coord_t y = bed_bbox.min.y(); y <= bed_bbox.max.y(); y += scale_(10.0)) { - Polyline line; - line.append(Point(bed_bbox.min.x(), y)); - line.append(Point(bed_bbox.max.x(), y)); - axes_lines.push_back(line); - } - - // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped - Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); - - // append bed contours - Lines contour_lines = to_lines(poly); - std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); - - if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) - BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";*/ -} - // Try to match the print bed shape with the shape of an active profile. If such a match exists, // return the print bed model. std::tuple Bed3D::detect_type(const Pointfs& shape) @@ -454,22 +455,22 @@ std::tuple Bed3D::detect_type(const Point return { Type::Custom, {}, {} }; } -void Bed3D::render_axes() const +void Bed3D::render_axes() { if (m_build_volume.valid()) m_axes.render(); } -void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom) { if (!bottom) - render_model(); + render_model(view_matrix, projection_matrix); /*if (show_texture) render_texture(bottom, canvas);*/ } -/*void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const +/*void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) { GLTexture* texture = const_cast(&m_texture); GLTexture* temp_texture = const_cast(&m_temp_texture); @@ -537,6 +538,9 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("transparent_background", bottom); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); @@ -624,9 +628,10 @@ void Bed3D::update_model_offset() const const_cast(m_extended_bounding_box) = calc_extended_bounding_box(); } -GeometryBuffer Bed3D::update_bed_triangles() const +void Bed3D::update_bed_triangles() { - GeometryBuffer new_triangles; + m_triangles.reset(); + Vec3d shift = m_extended_bounding_box.center(); shift(2) = -0.03; Vec3d* model_offset_ptr = const_cast(&m_model_offset); @@ -634,7 +639,7 @@ GeometryBuffer Bed3D::update_bed_triangles() const //BBS: TODO: hack for default bed BoundingBoxf3 build_volume; - if (!m_build_volume.valid()) return new_triangles; + if (!m_build_volume.valid()) return; auto bed_ext = get_extents(m_bed_shape); (*model_offset_ptr)(0) = m_build_volume.bounding_volume2d().min.x() - bed_ext.min.x(); (*model_offset_ptr)(1) = m_build_volume.bounding_volume2d().min.y() - bed_ext.min.y(); @@ -646,105 +651,115 @@ GeometryBuffer Bed3D::update_bed_triangles() const new_bed_shape.push_back(new_point); } ExPolygon poly{ Polygon::new_scale(new_bed_shape) }; - if (!new_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) { - ; + if (!init_model_from_poly(m_triangles, poly, GROUND_Z)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to update plate triangles\n"; } // update extended bounding box const_cast(m_extended_bounding_box) = calc_extended_bounding_box(); - return new_triangles; } -void Bed3D::render_model() const +void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix) { if (m_model_filename.empty()) return; - GLModel* model = const_cast(&m_model); - - if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) { - model->set_color(-1, m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR); + if (m_model.get_filename() != m_model_filename && m_model.init_from_file(m_model_filename)) { + m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR); update_model_offset(); + + // BBS: remove the bed picking logic + //register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset)); } - if (!model->get_filename().empty()) { + if (!m_model.get_filename().empty()) { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.0f); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z())); - model->render(); - glsafe(::glPopMatrix()); + const Transform3d model_matrix = Geometry::assemble_transform(m_model_offset); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_model.render(); shader->stop_using(); } } } -void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom) { if (m_model_filename.empty()) { - render_default(bottom); + render_default(bottom, view_matrix, projection_matrix); return; } if (!bottom) - render_model(); + render_model(view_matrix, projection_matrix); /*if (show_texture) render_texture(bottom, canvas);*/ } -void Bed3D::render_default(bool bottom) const +void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix) { - bool picking = false; - const_cast(&m_texture)->reset(); + m_texture.reset(); - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - GeometryBuffer default_triangles = update_bed_triangles(); - if (triangles_vcount > 0) { - bool has_model = !m_model.get_filename().empty(); + update_bed_triangles(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + shader->set_uniform("view_model_matrix", view_matrix); + shader->set_uniform("projection_matrix", projection_matrix); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - - if (!has_model && !bottom) { + if (m_model.get_filename().empty() && !bottom) { // draw background glsafe(::glDepthMask(GL_FALSE)); - glsafe(::glColor4fv(picking ? PICKING_MODEL_COLOR.data() : DEFAULT_MODEL_COLOR.data())); - glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); - glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)default_triangles.get_vertices_data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + m_triangles.set_color(DEFAULT_MODEL_COLOR); + m_triangles.render(); glsafe(::glDepthMask(GL_TRUE)); } /*if (!picking) { // draw grid glsafe(::glLineWidth(1.5f * m_scale_factor)); - if (has_model && !bottom) - glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f)); - else - glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f)); - glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); + m_gridlines.set_color(picking ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR); + m_gridlines.render(); }*/ - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glDisable(GL_BLEND)); + + shader->stop_using(); } } -void Bed3D::release_VBOs() +// BBS: remove the bed picking logic +/* +void Bed3D::register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo) { - if (m_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_vbo_id)); - m_vbo_id = 0; - } -} + assert(m_model.mesh_raycaster == nullptr); + indexed_triangle_set its; + its.vertices.reserve(geometry.vertices_count()); + for (size_t i = 0; i < geometry.vertices_count(); ++i) { + its.vertices.emplace_back(geometry.extract_position_3(i)); + } + its.indices.reserve(geometry.indices_count() / 3); + for (size_t i = 0; i < geometry.indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(geometry.extract_index(tri_id), geometry.extract_index(tri_id + 1), geometry.extract_index(tri_id + 2)); + } + + m_model.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + wxGetApp().plater()->canvas3D()->add_raycaster_for_picking(SceneRaycaster::EType::Bed, 0, *m_model.mesh_raycaster, trafo); +} +*/ } // GUI } // Slic3r diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index f6daadddf1..b668b5ed61 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -5,7 +5,8 @@ #include "3DScene.hpp" #include "GLModel.hpp" -#include +#include "libslic3r/BuildVolume.hpp" +#include "libslic3r/ExPolygon.hpp" #include #include @@ -15,6 +16,7 @@ namespace GUI { class GLCanvas3D; +/* class GeometryBuffer { struct Vertex @@ -38,13 +40,16 @@ public: size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); } unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); } }; +*/ + +bool init_model_from_poly(GLModel &model, const ExPolygon &poly, float z); class Bed3D { public: - static std::array AXIS_X_COLOR; - static std::array AXIS_Y_COLOR; - static std::array AXIS_Z_COLOR; + static ColorRGBA AXIS_X_COLOR; + static ColorRGBA AXIS_Y_COLOR; + static ColorRGBA AXIS_Z_COLOR; static void update_render_colors(); static void load_render_colors(); @@ -70,7 +75,7 @@ public: m_arrow.reset(); } float get_total_length() const { return m_stem_length + DefaultTipLength; } - void render() const; + void render(); }; public: @@ -91,14 +96,13 @@ private: BoundingBoxf3 m_extended_bounding_box; // Slightly expanded print bed polygon, for collision detection. //Polygon m_polygon; - GeometryBuffer m_triangles; - //GeometryBuffer m_gridlines; + GLModel m_triangles; + //GLModel m_gridlines; GLTexture m_texture; // temporary texture shown until the main texture has still no levels compressed //GLTexture m_temp_texture; GLModel m_model; Vec3d m_model_offset{ Vec3d::Zero() }; - unsigned int m_vbo_id{ 0 }; Axes m_axes; float m_scale_factor{ 1.0f }; @@ -109,7 +113,7 @@ private: public: Bed3D() = default; - ~Bed3D() { release_VBOs(); } + ~Bed3D() = default; // Update print bed model from configuration. // Return true if the bed shape changed, so the calee will update the UI. @@ -142,8 +146,7 @@ public: bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes); - //void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor); + void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes); void on_change_color_mode(bool is_dark); @@ -151,21 +154,21 @@ private: //BBS: add partplate related logic // Calculate an extended bounding box from axes and current model for visualization purposes. BoundingBoxf3 calc_extended_bounding_box(bool consider_model_offset = true) const; - void calc_triangles(const ExPolygon& poly); - void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); void update_model_offset() const; //BBS: with offset - GeometryBuffer update_bed_triangles() const; + void update_bed_triangles(); static std::tuple detect_type(const Pointfs& shape); - void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, + void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes); - void render_axes() const; - void render_system(GLCanvas3D& canvas, bool bottom) const; - //void render_texture(bool bottom, GLCanvas3D& canvas) const; - void render_model() const; - void render_custom(GLCanvas3D& canvas, bool bottom) const; - void render_default(bool bottom) const; - void release_VBOs(); + void render_axes(); + void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); + //void render_texture(bool bottom, GLCanvas3D& canvas); + void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); + void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); + void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix); + + // BBS: remove the bed picking logic + // void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo); }; } // GUI diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index be821f64fe..7325941ed0 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,18 +1,23 @@ -#include "slic3r/GUI/3DScene.hpp" +///|/ Copyright (c) Prusa Research 2016 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2017 Eyal Soha @eyal0 +///|/ Copyright (c) Slic3r 2015 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/GUI/3DScene.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2013 Guillaume Seguin @iXce +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include -#if ENABLE_SMOOTH_NORMALS -#include -#include -#include -#endif // ENABLE_SMOOTH_NORMALS - #include "3DScene.hpp" #include "GLShader.hpp" #include "GUI_App.hpp" #include "GUI_Colors.hpp" #include "Plater.hpp" #include "BitmapCache.hpp" +#include "Camera.hpp" #include "libslic3r/BuildVolume.hpp" #include "libslic3r/ExtrusionEntity.hpp" @@ -70,262 +75,32 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char #endif // HAS_GLSAFE // BBS -std::vector> get_extruders_colors() +std::vector get_extruders_colors() { - unsigned char rgba_color[4] = {}; - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - std::vector> colors_out(colors.size()); + Slic3r::ColorRGBA rgba_color; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::vector colors_out(colors.size()); for (const std::string &color : colors) { - Slic3r::GUI::BitmapCache::parse_color4(color, rgba_color); + Slic3r::decode_color(color, rgba_color); size_t color_idx = &color - &colors.front(); - colors_out[color_idx] = { - float(rgba_color[0]) / 255.f, - float(rgba_color[1]) / 255.f, - float(rgba_color[2]) / 255.f, - float(rgba_color[3]) / 255.f, - }; + colors_out[color_idx] = rgba_color; } return colors_out; } float FullyTransparentMaterialThreshold = 0.1f; float FullTransparentModdifiedToFixAlpha = 0.3f; -std::array adjust_color_for_rendering(const std::array &colors) + +Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors) { - if (colors[3] < FullyTransparentMaterialThreshold) { // completely transparent - std::array new_color; - new_color[0] = 1; - new_color[1] = 1; - new_color[2] = 1; - new_color[3] = FullTransparentModdifiedToFixAlpha; - return new_color; - } + if (colors.a() < FullyTransparentMaterialThreshold) { // completely transparent + return {1, 1, 1, FullTransparentModdifiedToFixAlpha}; + } return colors; } namespace Slic3r { -#if ENABLE_SMOOTH_NORMALS -static void smooth_normals_corner(TriangleMesh& mesh, std::vector& normals) -{ - using MapMatrixXfUnaligned = Eigen::Map>; - using MapMatrixXiUnaligned = Eigen::Map>; - - std::vector face_normals = its_face_normals(mesh.its); - - Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), - Eigen::Index(mesh.its.vertices.size()), 3).cast(); - Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), - Eigen::Index(mesh.its.indices.size()), 3); - Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), - Eigen::Index(face_normals.size()), 3).cast(); - Eigen::MatrixXd out_normals; - - igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); - - normals = std::vector(mesh.its.vertices.size()); - for (size_t i = 0; i < mesh.its.indices.size(); ++i) { - for (size_t j = 0; j < 3; ++j) { - normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast(); - } - } -} - -static void smooth_normals_vertex(TriangleMesh& mesh, std::vector& normals) -{ - using MapMatrixXfUnaligned = Eigen::Map>; - using MapMatrixXiUnaligned = Eigen::Map>; - - Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), - Eigen::Index(mesh.its.vertices.size()), 3).cast(); - Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), - Eigen::Index(mesh.its.indices.size()), 3); - Eigen::MatrixXd out_normals; - -// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM, out_normals); -// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA, out_normals); - igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE, out_normals); -// igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT, out_normals); - - normals = std::vector(mesh.its.vertices.size()); - for (size_t i = 0; i < static_cast(out_normals.rows()); ++i) { - normals[i] = out_normals.row(i).cast(); - } -} -#endif // ENABLE_SMOOTH_NORMALS - -#if ENABLE_SMOOTH_NORMALS -void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals) -#else -void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh) -#endif // ENABLE_SMOOTH_NORMALS -{ - assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); - assert(quad_indices.empty() && triangle_indices_size == 0); - assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); - -#if ENABLE_SMOOTH_NORMALS - if (smooth_normals) { - TriangleMesh new_mesh(mesh); - std::vector normals; - smooth_normals_corner(new_mesh, normals); -// smooth_normals_vertex(new_mesh, normals); - - this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 2 * new_mesh.its.vertices.size()); - for (size_t i = 0; i < new_mesh.its.vertices.size(); ++i) { - const stl_vertex& v = new_mesh.its.vertices[i]; - const stl_normal& n = normals[i]; - this->push_geometry(v(0), v(1), v(2), n(0), n(1), n(2)); - } - - for (size_t i = 0; i < new_mesh.its.indices.size(); ++i) { - const stl_triangle_vertex_indices& idx = new_mesh.its.indices[i]; - this->push_triangle(idx(0), idx(1), idx(2)); - } - } - else { -#endif // ENABLE_SMOOTH_NORMALS - this->load_its_flat_shading(mesh.its); -#if ENABLE_SMOOTH_NORMALS - } -#endif // ENABLE_SMOOTH_NORMALS -} - -void GLIndexedVertexArray::load_its_flat_shading(const indexed_triangle_set &its) -{ - this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * its.indices.size()); - unsigned int vertices_count = 0; - for (int i = 0; i < int(its.indices.size()); ++ i) { - stl_triangle_vertex_indices face = its.indices[i]; - stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; - stl_vertex n = face_normal_normalized(vertex); - for (int j = 0; j < 3; ++j) - this->push_geometry(vertex[j](0), vertex[j](1), vertex[j](2), n(0), n(1), n(2)); - this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); - vertices_count += 3; - } - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, indices size %2%, vertices %3%, triangles %4% ") - %this %its.indices.size() %this->vertices_and_normals_interleaved.size() %this->triangle_indices.size() ; -} - -void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized) -{ - assert(this->vertices_and_normals_interleaved_VBO_id == 0); - assert(this->triangle_indices_VBO_id == 0); - assert(this->quad_indices_VBO_id == 0); - - if (! opengl_initialized) { - // Shrink the data vectors to conserve memory in case the data cannot be transfered to the OpenGL driver yet. - this->shrink_to_fit(); - return; - } - - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1% ") %this; - if (! this->vertices_and_normals_interleaved.empty()) { - glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - this->vertices_and_normals_interleaved.clear(); - } - if (! this->triangle_indices.empty()) { - glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - this->triangle_indices.clear(); - } - if (! this->quad_indices.empty()) { - glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - this->quad_indices.clear(); - } -} - -void GLIndexedVertexArray::release_geometry() -{ - if (this->vertices_and_normals_interleaved_VBO_id) { - glsafe(::glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); - this->vertices_and_normals_interleaved_VBO_id = 0; - } - if (this->triangle_indices_VBO_id) { - glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id)); - this->triangle_indices_VBO_id = 0; - } - if (this->quad_indices_VBO_id) { - glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id)); - this->quad_indices_VBO_id = 0; - } - this->clear(); -} - -void GLIndexedVertexArray::render() const -{ - assert(this->vertices_and_normals_interleaved_VBO_id != 0); - assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); - glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - - // Render using the Vertex Buffer Objects. - if (this->triangle_indices_size > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); - glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr)); - glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - if (this->quad_indices_size > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); - glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr)); - glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); -} - -void GLIndexedVertexArray::render( - const std::pair& tverts_range, - const std::pair& qverts_range) const -{ - // this method has been called before calling finalize() ? - if (this->vertices_and_normals_interleaved_VBO_id == 0 && !this->vertices_and_normals_interleaved.empty()) - return; - - assert(this->vertices_and_normals_interleaved_VBO_id != 0); - assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); - - // Render using the Vertex Buffer Objects. - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); - glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - - if (this->triangle_indices_size > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); - glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4))); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - if (this->quad_indices_size > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); - glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4))); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); -} const float GLVolume::SinkingContours::HalfWidth = 0.25f; @@ -333,18 +108,22 @@ void GLVolume::SinkingContours::render() { update(); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_shift.x(), m_shift.y(), m_shift.z())); + GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); + if (shader == nullptr) + return; + + const GUI::Camera& camera = GUI::wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::assemble_transform(m_shift)); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); m_model.render(); - glsafe(::glPopMatrix()); } void GLVolume::SinkingContours::update() { - int object_idx = m_parent.object_idx(); - Model& model = GUI::wxGetApp().plater()->model(); + const int object_idx = m_parent.object_idx(); + const Model& model = GUI::wxGetApp().plater()->model(); - if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { + if (0 <= object_idx && object_idx < int(model.objects.size()) && m_parent.is_sinking() && !m_parent.is_below_printbed()) { const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { m_old_box = box; @@ -353,28 +132,25 @@ void GLVolume::SinkingContours::update() const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh(); m_model.reset(); - GUI::GLModel::InitializationData init_data; + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + unsigned int vertices_counter = 0; MeshSlicingParams slicing_params; slicing_params.trafo = m_parent.world_matrix(); - Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params)); - for (ExPolygon &expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) { - GUI::GLModel::InitializationData::Entity entity; - entity.type = GUI::GLModel::PrimitiveType::Triangles; + const Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params)); + for (const ExPolygon& expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) { const std::vector triangulation = triangulate_expolygon_3d(expoly); + init_data.reserve_vertices(init_data.vertices_count() + triangulation.size()); + init_data.reserve_indices(init_data.indices_count() + triangulation.size()); for (const Vec3d& v : triangulation) { - entity.positions.emplace_back(v.cast() + Vec3f(0.0f, 0.0f, 0.015f)); // add a small positive z to avoid z-fighting - entity.normals.emplace_back(Vec3f::UnitZ()); - const size_t positions_count = entity.positions.size(); - if (positions_count % 3 == 0) { - entity.indices.emplace_back(positions_count - 3); - entity.indices.emplace_back(positions_count - 2); - entity.indices.emplace_back(positions_count - 1); - } + init_data.add_vertex((Vec3f)(v.cast() + 0.015f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting + ++vertices_counter; + if (vertices_counter % 3 == 0) + init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } - init_data.entities.emplace_back(entity); } - - m_model.init_from(init_data); + m_model.init_from(std::move(init_data)); } else m_shift = box.center() - m_old_box.center(); @@ -383,21 +159,21 @@ void GLVolume::SinkingContours::update() m_model.reset(); } -std::array GLVolume::DISABLED_COLOR = { 0.25f, 0.25f, 0.25f, 1.0f }; -std::array GLVolume::SLA_SUPPORT_COLOR = { 0.75f, 0.75f, 0.75f, 1.0f }; -std::array GLVolume::SLA_PAD_COLOR = { 0.0f, 0.2f, 0.0f, 1.0f }; +ColorRGBA GLVolume::DISABLED_COLOR = ColorRGBA::DARK_GRAY(); +ColorRGBA GLVolume::SLA_SUPPORT_COLOR = ColorRGBA::LIGHT_GRAY(); +ColorRGBA GLVolume::SLA_PAD_COLOR = { 0.0f, 0.2f, 0.0f, 1.0f }; // BBS -std::array GLVolume::NEUTRAL_COLOR = { 0.8f, 0.8f, 0.8f, 1.0f }; -std::array GLVolume::UNPRINTABLE_COLOR = { 0.0f, 0.0f, 0.0f, 0.5f }; +ColorRGBA GLVolume::NEUTRAL_COLOR = { 0.8f, 0.8f, 0.8f, 1.0f }; +ColorRGBA GLVolume::UNPRINTABLE_COLOR = { 0.0f, 0.0f, 0.0f, 0.5f }; -std::array GLVolume::MODEL_MIDIFIER_COL = {1.0f, 1.0f, 0.0f, 0.6f}; -std::array GLVolume::MODEL_NEGTIVE_COL = {0.3f, 0.3f, 0.3f, 0.4f}; -std::array GLVolume::SUPPORT_ENFORCER_COL = {0.3f, 0.3f, 1.0f, 0.4f}; -std::array GLVolume::SUPPORT_BLOCKER_COL = {1.0f, 0.3f, 0.3f, 0.4f}; +ColorRGBA GLVolume::MODEL_MIDIFIER_COL = {1.0f, 1.0f, 0.0f, 0.6f}; +ColorRGBA GLVolume::MODEL_NEGTIVE_COL = {0.3f, 0.3f, 0.3f, 0.4f}; +ColorRGBA GLVolume::SUPPORT_ENFORCER_COL = {0.3f, 0.3f, 1.0f, 0.4f}; +ColorRGBA GLVolume::SUPPORT_BLOCKER_COL = {1.0f, 0.3f, 0.3f, 0.4f}; -std::array GLVolume::MODEL_HIDDEN_COL = {0.f, 0.f, 0.f, 0.3f}; +ColorRGBA GLVolume::MODEL_HIDDEN_COL = {0.f, 0.f, 0.f, 0.3f}; -std::array, 5> GLVolume::MODEL_COLOR = { { +std::array GLVolume::MODEL_COLOR = { { { 1.0f, 1.0f, 0.0f, 1.f }, { 1.0f, 0.5f, 0.5f, 1.f }, { 0.5f, 1.0f, 0.5f, 1.f }, @@ -407,25 +183,25 @@ std::array, 5> GLVolume::MODEL_COLOR = { { void GLVolume::update_render_colors() { - GLVolume::DISABLED_COLOR = GLColor(RenderColor::colors[RenderCol_Model_Disable]); - GLVolume::NEUTRAL_COLOR = GLColor(RenderColor::colors[RenderCol_Model_Neutral]); - GLVolume::MODEL_COLOR[0] = GLColor(RenderColor::colors[RenderCol_Modifier]); - GLVolume::MODEL_COLOR[1] = GLColor(RenderColor::colors[RenderCol_Negtive_Volume]); - GLVolume::MODEL_COLOR[2] = GLColor(RenderColor::colors[RenderCol_Support_Enforcer]); - GLVolume::MODEL_COLOR[3] = GLColor(RenderColor::colors[RenderCol_Support_Blocker]); - GLVolume::UNPRINTABLE_COLOR = GLColor(RenderColor::colors[RenderCol_Model_Unprintable]); + GLVolume::DISABLED_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Disable]); + GLVolume::NEUTRAL_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Neutral]); + GLVolume::MODEL_COLOR[0] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Modifier]); + GLVolume::MODEL_COLOR[1] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Negtive_Volume]); + GLVolume::MODEL_COLOR[2] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Support_Enforcer]); + GLVolume::MODEL_COLOR[3] = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Support_Blocker]); + GLVolume::UNPRINTABLE_COLOR = GUI::ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Model_Unprintable]); } void GLVolume::load_render_colors() { - RenderColor::colors[RenderCol_Model_Disable] = IMColor(GLVolume::DISABLED_COLOR); - RenderColor::colors[RenderCol_Model_Neutral] = IMColor(GLVolume::NEUTRAL_COLOR); - RenderColor::colors[RenderCol_Modifier] = IMColor(GLVolume::MODEL_COLOR[0]); - RenderColor::colors[RenderCol_Negtive_Volume] = IMColor(GLVolume::MODEL_COLOR[1]); - RenderColor::colors[RenderCol_Support_Enforcer] = IMColor(GLVolume::MODEL_COLOR[2]); - RenderColor::colors[RenderCol_Support_Blocker] = IMColor(GLVolume::MODEL_COLOR[3]); - RenderColor::colors[RenderCol_Model_Unprintable]= IMColor(GLVolume::UNPRINTABLE_COLOR); + RenderColor::colors[RenderCol_Model_Disable] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::DISABLED_COLOR); + RenderColor::colors[RenderCol_Model_Neutral] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::NEUTRAL_COLOR); + RenderColor::colors[RenderCol_Modifier] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[0]); + RenderColor::colors[RenderCol_Negtive_Volume] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[1]); + RenderColor::colors[RenderCol_Support_Enforcer] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[2]); + RenderColor::colors[RenderCol_Support_Blocker] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::MODEL_COLOR[3]); + RenderColor::colors[RenderCol_Model_Unprintable] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::UNPRINTABLE_COLOR); } GLVolume::GLVolume(float r, float g, float b, float a) @@ -451,33 +227,19 @@ GLVolume::GLVolume(float r, float g, float b, float a) , force_native_color(false) , force_neutral_color(false) , force_sinking_contours(false) + , picking(false) , tverts_range(0, size_t(-1)) - , qverts_range(0, size_t(-1)) { color = { r, g, b, a }; set_render_color(color); mmuseg_ts = 0; } -void GLVolume::set_color(const std::array& rgba) -{ - color = rgba; -} // BBS float GLVolume::explosion_ratio = 1.0; float GLVolume::last_explosion_ratio = 1.0; -void GLVolume::set_render_color(float r, float g, float b, float a) -{ - render_color = { r, g, b, a }; -} - -void GLVolume::set_render_color(const std::array& rgba) -{ - render_color = rgba; -} - void GLVolume::set_render_color() { bool outside = is_outside || is_below_printbed(); @@ -514,57 +276,45 @@ void GLVolume::set_render_color() #endif else { //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(color); + ColorRGBA new_color = adjust_color_for_rendering(color); set_render_color(new_color); } } if (force_transparent) { - if (color[3] < FullyTransparentMaterialThreshold) { - render_color[3] = FullTransparentModdifiedToFixAlpha; + if (color.a() < FullyTransparentMaterialThreshold) { + render_color.a(FullTransparentModdifiedToFixAlpha); } else { - render_color[3] = color[3]; + render_color.a(color.a()); } } //BBS set unprintable color if (!printable) { - render_color[0] = UNPRINTABLE_COLOR[0]; - render_color[1] = UNPRINTABLE_COLOR[1]; - render_color[2] = UNPRINTABLE_COLOR[2]; - render_color[3] = UNPRINTABLE_COLOR[3]; + render_color = UNPRINTABLE_COLOR; } //BBS set invisible color if (!visible) { - render_color[0] = MODEL_HIDDEN_COL[0]; - render_color[1] = MODEL_HIDDEN_COL[1]; - render_color[2] = MODEL_HIDDEN_COL[2]; - render_color[3] = MODEL_HIDDEN_COL[3]; + render_color = MODEL_HIDDEN_COL; } } -std::array color_from_model_volume(const ModelVolume& model_volume) +ColorRGBA color_from_model_volume(const ModelVolume& model_volume) { - std::array color = {0.0f, 0.0f, 0.0f, 1.0f}; - if (model_volume.is_negative_volume()) { + ColorRGBA color; + if (model_volume.is_negative_volume()) return GLVolume::MODEL_NEGTIVE_COL; - } - else if (model_volume.is_modifier()) { + else if (model_volume.is_modifier()) #if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT return GLVolume::MODEL_MIDIFIER_COL; #else - color[0] = 0.2f; - color[1] = 1.0f; - color[2] = 0.2f; + color = { 0.2f, 1.0f, 0.2f, 1.0f }; #endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT - } - else if (model_volume.is_support_blocker()) { + else if (model_volume.is_support_blocker()) return GLVolume::SUPPORT_BLOCKER_COL; - } - else if (model_volume.is_support_enforcer()) { + else if (model_volume.is_support_enforcer()) return GLVolume::SUPPORT_ENFORCER_COL; - } return color; } @@ -631,281 +381,100 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const void GLVolume::set_range(double min_z, double max_z) { - this->qverts_range.first = 0; - this->qverts_range.second = this->indexed_vertex_array.quad_indices_size; this->tverts_range.first = 0; - this->tverts_range.second = this->indexed_vertex_array.triangle_indices_size; - if (! this->print_zs.empty()) { + this->tverts_range.second = this->model.indices_count(); + + if (!this->print_zs.empty()) { // The Z layer range is specified. // First test whether the Z span of this object is not out of (min_z, max_z) completely. - if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) { - this->qverts_range.second = 0; + if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) this->tverts_range.second = 0; - } else { + else { // Then find the lowest layer to be displayed. size_t i = 0; - for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); - if (i == this->print_zs.size()) { + for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++i); + if (i == this->print_zs.size()) // This shall not happen. - this->qverts_range.second = 0; this->tverts_range.second = 0; - } else { + else { // Remember start of the layer. - this->qverts_range.first = this->offsets[i * 2]; - this->tverts_range.first = this->offsets[i * 2 + 1]; + this->tverts_range.first = this->offsets[i]; // Some layers are above $min_z. Which? - for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++ i); - if (i < this->print_zs.size()) { - this->qverts_range.second = this->offsets[i * 2]; - this->tverts_range.second = this->offsets[i * 2 + 1]; - } + for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++i); + if (i < this->print_zs.size()) + this->tverts_range.second = this->offsets[i]; } } } } -//BBS: add outline related logic -//static unsigned char stencil_data[1284][2944]; -void GLVolume::render(bool with_outline) const +void GLVolume::render() { if (!is_active) return; - if (this->is_left_handed()) - glFrontFace(GL_CW); - glsafe(::glCullFace(GL_BACK)); - glsafe(::glPushMatrix()); + GLShaderProgram *shader = GUI::wxGetApp().get_current_shader(); + if (shader == nullptr) + return; - // BBS: add logic for mmu segmentation rendering - auto render_body = [&]() { - bool color_volume = false; - ModelObjectPtrs& model_objects = GUI::wxGetApp().model().objects; - do { - if ((!printable) || object_idx() >= model_objects.size()) - break; + ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects; + std::vector colors = get_extruders_colors(); - ModelObject* mo = model_objects[object_idx()]; - if (volume_idx() >= mo->volumes.size()) - break; + simple_render(shader, model_objects, colors); +} - ModelVolume* mv = mo->volumes[volume_idx()]; - if (mv->mmu_segmentation_facets.empty()) - break; +//BBS: add outline related logic +void GLVolume::render_with_outline(const Transform3d &view_model_matrix) +{ + if (!is_active) + return; - color_volume = true; - if (mv->mmu_segmentation_facets.timestamp() != mmuseg_ts) { - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, current mmuseg_ts %3%, current color size %4%") - %this %this->name %mmuseg_ts %mmuseg_ivas.size() ; - mmuseg_ivas.clear(); - std::vector its_per_color; - mv->mmu_segmentation_facets.get_facets(*mv, its_per_color); - mmuseg_ivas.resize(its_per_color.size()); - for (int idx = 0; idx < its_per_color.size(); idx++) { - mmuseg_ivas[idx].load_its_flat_shading(its_per_color[idx]); - mmuseg_ivas[idx].finalize_geometry(true); - } + GLShaderProgram *shader = GUI::wxGetApp().get_current_shader(); + if (shader == nullptr) + return; - mmuseg_ts = mv->mmu_segmentation_facets.timestamp(); - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, new mmuseg_ts %3%, new color size %4%") - %this %this->name %mmuseg_ts %mmuseg_ivas.size(); - } - } while (0); + ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects; + std::vector colors = get_extruders_colors(); - if (color_volume) { - GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); - std::vector> colors = get_extruders_colors(); + glEnable(GL_STENCIL_TEST); + glStencilMask(0xFF); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glClear(GL_STENCIL_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 0xff, 0xFF); - //when force_transparent, we need to keep the alpha - if (force_native_color && (render_color[3] < 1.0)) { - for (int index = 0; index < colors.size(); index ++) - colors[index][3] = render_color[3]; - } - glsafe(::glMultMatrixd(world_matrix().data())); - for (int idx = 0; idx < mmuseg_ivas.size(); idx++) { - GLIndexedVertexArray& iva = mmuseg_ivas[idx]; - if (iva.triangle_indices_size == 0 && iva.quad_indices_size == 0) - continue; + simple_render(shader, model_objects, colors); - if (shader) { - if (idx == 0) { - ModelObject* mo = model_objects[object_idx()]; - ModelVolume* mv = mo->volumes[volume_idx()]; - int extruder_id = mv->extruder_id(); - //shader->set_uniform("uniform_color", colors[extruder_id - 1]); - //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(colors[extruder_id - 1]); - shader->set_uniform("uniform_color", new_color); - } - else { - if (idx <= colors.size()) { - //shader->set_uniform("uniform_color", colors[idx - 1]); - //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(colors[idx - 1]); - shader->set_uniform("uniform_color", new_color); - } - else { - //shader->set_uniform("uniform_color", colors[0]); - //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(colors[0]); - shader->set_uniform("uniform_color", new_color); - } - } - } - iva.render(this->tverts_range, this->qverts_range); - /*if (force_native_color && (render_color[3] < 1.0)) { - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< boost::format(", this %1%, name %2%, tverts_range {%3,%4}, qverts_range{%5%, %6%}") - %this %this->name %this->tverts_range.first %this->tverts_range.second - % this->qverts_range.first % this->qverts_range.second; - }*/ - } - } - else { - glsafe(::glMultMatrixd(world_matrix().data())); - this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); - } - }; + // 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing. + // Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing + // the objects' size differences, making it look like borders. + glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF); + glStencilMask(0x00); + float scale = 1.02f; + ColorRGBA body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red - //BBS: add logic of outline rendering - GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); - //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, with_outline %2%, shader %3%.")%__LINE__ %with_outline %shader; - if (with_outline && shader != nullptr) - { - do - { - glEnable(GL_STENCIL_TEST); - glStencilMask(0xFF); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0xff, 0xFF); - //another way use depth buffer - //glsafe(::glEnable(GL_DEPTH_TEST)); - //glsafe(::glDepthFunc(GL_ALWAYS)); - //glsafe(::glDepthMask(GL_FALSE)); - //glsafe(::glEnable(GL_BLEND)); - //glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + model.set_color(body_color); + shader->set_uniform("is_outline", true); - /*GLShaderProgram* outline_shader = GUI::wxGetApp().get_shader("outline"); - if (outline_shader == nullptr) - { - glDisable(GL_STENCIL_TEST); - this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); - break; - } - shader->stop_using(); - outline_shader->start_using(); - //float scale_ratio = 1.02f; - std::array outline_color = { 0.0f, 1.0f, 0.0f, 1.0f }; + Transform3d matrix = view_model_matrix; + matrix.scale(scale); + shader->set_uniform("view_model_matrix", matrix); + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); - outline_shader->set_uniform("uniform_color", outline_color);*/ -#if 0 //dump stencil buffer - int i = 100, j = 100; - std::string file_name; - FILE* file = NULL; - memset(stencil_data, 0, sizeof(stencil_data)); - glReadPixels(0, 0, 2936, 1083, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencil_data); - for (i = 100; i < 1083; i++) - { - for (j = 100; j < 2936; j++) - { - if (stencil_data[i][j] != 0) - { - file_name = "before_stencil_index_" + std::to_string(i) + "x" + std::to_string(j) + ".a8"; - break; - } - } + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("is_outline", false); - if (stencil_data[i][j] != 0) - break; - } - file = fopen(file_name.c_str(), "w"); - if (file) - { - fwrite(stencil_data, 2936 * 1083, 1, file); - fclose(file); - } -#endif - render_body(); - //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render body, shader name %2%")%__LINE__ %shader->get_name(); - -#if 0 //dump stencil buffer after first rendering - memset(stencil_data, 0, sizeof(stencil_data)); - glReadPixels(0, 0, 2936, 1083, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencil_data); - for (i = 100; i < 1083; i++) - { - for (j = 100; j < 2936; j++) - if (stencil_data[i][j] != 0) - { - file_name = "after_stencil_index_" + std::to_string(i) + "x" + std::to_string(j) + ".a8"; - break; - } - - if (stencil_data[i][j] != 0) - break; - } - - file = fopen(file_name.c_str(), "w"); - if (file) - { - fwrite(stencil_data, 2936 * 1083, 1, file); - fclose(file); - } -#endif - // 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing. - // Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing - // the objects' size differences, making it look like borders. - // ----------------------------------------------------------------------------------------------------------------------------- - /*GLShaderProgram* outline_shader = GUI::wxGetApp().get_shader("outline"); - if (outline_shader == nullptr) - { - glDisable(GL_STENCIL_TEST); - break; - } - shader->stop_using(); - outline_shader->start_using();*/ - //outline_shader->stop_using(); - //shader->start_using(); - - glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF); - glStencilMask(0x00); - float scale = 1.02f; - std::array body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red - - shader->set_uniform("uniform_color", body_color); - shader->set_uniform("is_outline", true); - glsafe(::glPopMatrix()); - glsafe(::glPushMatrix()); - - Transform3d matrix = world_matrix(); - matrix.scale(scale); - glsafe(::glMultMatrixd(matrix.data())); - this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); - //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render for body, shader name %2%")%__LINE__ %shader->get_name(); - shader->set_uniform("is_outline", false); - - //glStencilMask(0xFF); - //glStencilFunc(GL_ALWAYS, 0, 0xFF); - glDisable(GL_STENCIL_TEST); - //glEnable(GL_DEPTH_TEST); - //outline_shader->stop_using(); - //shader->start_using(); - } while (0); - } - else { - render_body(); - //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, normal render.")%__LINE__; - } - glsafe(::glPopMatrix()); - if (this->is_left_handed()) - glFrontFace(GL_CCW); + glDisable(GL_STENCIL_TEST); } //BBS add render for simple case -void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector>& extruder_colors) const +void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector extruder_colors) { if (this->is_left_handed()) glFrontFace(GL_CW); glsafe(::glCullFace(GL_BACK)); - glsafe(::glPushMatrix()); bool color_volume = false; ModelObject* model_object = nullptr; @@ -923,57 +492,59 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj color_volume = true; if (model_volume->mmu_segmentation_facets.timestamp() != mmuseg_ts) { - mmuseg_ivas.clear(); + mmuseg_models.clear(); std::vector its_per_color; model_volume->mmu_segmentation_facets.get_facets(*model_volume, its_per_color); - mmuseg_ivas.resize(its_per_color.size()); + mmuseg_models.resize(its_per_color.size()); for (int idx = 0; idx < its_per_color.size(); idx++) { - mmuseg_ivas[idx].load_its_flat_shading(its_per_color[idx]); - mmuseg_ivas[idx].finalize_geometry(true); + mmuseg_models[idx].init_from(its_per_color[idx]); } mmuseg_ts = model_volume->mmu_segmentation_facets.timestamp(); } } while (0); - if (color_volume) { - glsafe(::glMultMatrixd(world_matrix().data())); - for (int idx = 0; idx < mmuseg_ivas.size(); idx++) { - GLIndexedVertexArray& iva = mmuseg_ivas[idx]; - if (iva.triangle_indices_size == 0 && iva.quad_indices_size == 0) + if (color_volume && !picking) { + // when force_transparent, we need to keep the alpha + if (force_native_color && render_color.is_transparent()) { + for (auto &extruder_color : extruder_colors) + extruder_color.a(render_color.a()); + } + + for (int idx = 0; idx < mmuseg_models.size(); idx++) { + GUI::GLModel &m = mmuseg_models[idx]; + if (!m.is_initialized()) continue; - if (shader) { - if (idx == 0) { - int extruder_id = model_volume->extruder_id(); + if (idx == 0) { + int extruder_id = model_volume->extruder_id(); + //to make black not too hard too see + ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[extruder_id - 1]); + m.set_color(new_color); + } + else { + if (idx <= extruder_colors.size()) { //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(extruder_colors[extruder_id - 1]); - shader->set_uniform("uniform_color", new_color); + ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[idx - 1]); + m.set_color(new_color); } else { - if (idx <= extruder_colors.size()) { - //shader->set_uniform("uniform_color", extruder_colors[idx - 1]); - //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(extruder_colors[idx - 1]); - shader->set_uniform("uniform_color", new_color); - } - else { - //shader->set_uniform("uniform_color", extruder_colors[0]); - //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(extruder_colors[0]); - shader->set_uniform("uniform_color", new_color); - } + //to make black not too hard too see + ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[0]); + m.set_color(new_color); } } - iva.render(this->tverts_range, this->qverts_range); + if (tverts_range == std::make_pair(0, -1)) + m.render(); + else + m.render(this->tverts_range); } + } else { + if (tverts_range == std::make_pair(0, -1)) + model.render(); + else + model.render(this->tverts_range); } - else { - glsafe(::glMultMatrixd(world_matrix().data())); - this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); - } - - glsafe(::glPopMatrix()); if (this->is_left_handed()) glFrontFace(GL_CCW); } @@ -999,43 +570,41 @@ void GLVolume::render_sinking_contours() m_sinking_contours.render(); } -GLWipeTowerVolume::GLWipeTowerVolume(const std::vector>& colors) +GLWipeTowerVolume::GLWipeTowerVolume(const std::vector& colors) : GLVolume() { m_colors = colors; } -void GLWipeTowerVolume::render(bool with_outline) const +void GLWipeTowerVolume::render() { if (!is_active) return; - if (m_colors.size() == 0 || m_colors.size() != iva_per_colors.size()) + if (m_colors.size() == 0 || m_colors.size() != model_per_colors.size()) return; if (this->is_left_handed()) glFrontFace(GL_CW); glsafe(::glCullFace(GL_BACK)); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(world_matrix().data())); - GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); for (int i = 0; i < m_colors.size(); i++) { - if (shader) { - std::array new_color = adjust_color_for_rendering(m_colors[i]); - shader->set_uniform("uniform_color", new_color); + if (!picking) { + ColorRGBA new_color = adjust_color_for_rendering(m_colors[i]); + this->model_per_colors[i].set_color(new_color); + } else { + this->model_per_colors[i].set_color(model.get_color()); } - this->iva_per_colors[i].render(); + this->model_per_colors[i].render(); } - glsafe(::glPopMatrix()); if (this->is_left_handed()) glFrontFace(GL_CCW); } bool GLWipeTowerVolume::IsTransparent() { for (size_t i = 0; i < m_colors.size(); i++) { - if (m_colors[i][3] < 1.0f) { + if (m_colors[i].is_transparent()) { return true; } } @@ -1043,48 +612,42 @@ bool GLWipeTowerVolume::IsTransparent() { } std::vector GLVolumeCollection::load_object( - const ModelObject *model_object, - int obj_idx, - const std::vector &instance_idxs, - const std::string &color_by, - bool opengl_initialized) + const ModelObject* model_object, + int obj_idx, + const std::vector& instance_idxs) { std::vector volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int instance_idx : instance_idxs) - volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized)); + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx)); return volumes_idx; } int GLVolumeCollection::load_object_volume( - const ModelObject *model_object, + const ModelObject* model_object, int obj_idx, int volume_idx, int instance_idx, - const std::string &color_by, - bool opengl_initialized, bool in_assemble_view, bool use_loaded_id) { const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; - const TriangleMesh &mesh = model_volume->mesh(); - std::array color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4]; - color[3] = model_volume->is_model_part() ? 0.7f : 0.4f; - this->volumes.emplace_back(new GLVolume(color)); + std::shared_ptr mesh = model_volume->mesh_ptr(); + this->volumes.emplace_back(new GLVolume()); GLVolume& v = *this->volumes.back(); v.set_color(color_from_model_volume(*model_volume)); v.name = model_volume->name; + #if ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.load_mesh(mesh, true); + v.model.init_from(mesh, true); #else - v.indexed_vertex_array.load_mesh(mesh); + v.model.init_from(*mesh); + v.mesh_raycaster = std::make_unique(mesh); #endif // ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); - if (model_volume->is_model_part()) - { + if (model_volume->is_model_part()) { // GLVolume will reference a convex hull from model_volume! v.set_convex_hull(model_volume->get_convex_hull_shared_ptr()); if (extruder_id != -1) @@ -1112,14 +675,13 @@ int GLVolumeCollection::load_object_volume( // This function produces volumes for multiple instances in a single shot, // as some object specific mesh conversions may be expensive. void GLVolumeCollection::load_object_auxiliary( - const SLAPrintObject *print_object, + const SLAPrintObject* print_object, int obj_idx, // pairs of const std::vector>& instances, SLAPrintObjectStep milestone, // Timestamp of the last change of the milestone - size_t timestamp, - bool opengl_initialized) + size_t timestamp) { assert(print_object->is_step_done(milestone)); Transform3d mesh_trafo_inv = print_object->trafo().inverse(); @@ -1133,11 +695,12 @@ void GLVolumeCollection::load_object_auxiliary( this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); #if ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.load_mesh(mesh, true); + v.model.init_from(mesh, true); #else - v.indexed_vertex_array.load_mesh(mesh); + v.model.init_from(mesh); + v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); + v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); #endif // ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. @@ -1155,7 +718,7 @@ void GLVolumeCollection::load_object_auxiliary( int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, - float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized) + float rotation_angle, bool size_unknown, float brim_width) { int plate_idx = obj_idx - 1000; @@ -1164,8 +727,8 @@ int GLVolumeCollection::load_wipe_tower_preview( if (height == 0.0f) height = 0.1f; - std::vector> extruder_colors = get_extruders_colors(); - std::vector> colors; + std::vector extruder_colors = get_extruders_colors(); + std::vector colors; GUI::PartPlateList& ppl = GUI::wxGetApp().plater()->get_partplate_list(); std::vector plate_extruders = ppl.get_plate(plate_idx)->get_extruders(true); TriangleMesh wipe_tower_shell = make_cube(width, depth, height); @@ -1176,27 +739,19 @@ int GLVolumeCollection::load_wipe_tower_preview( colors.push_back(extruder_colors[0]); } -#if 0 - // We'll make another mesh to show the brim (fixed layer height): - TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f); - brim_mesh.translate(-brim_width, -brim_width, 0.f); - mesh.merge(brim_mesh); -#endif - // Orca: make it transparent for(auto& color : colors) - color[3] = 0.66f; + color.a(0.66f); volumes.emplace_back(new GLWipeTowerVolume(colors)); GLWipeTowerVolume& v = *dynamic_cast(volumes.back()); - v.iva_per_colors.resize(colors.size()); + v.model_per_colors.resize(colors.size()); for (int i = 0; i < colors.size(); i++) { TriangleMesh color_part = make_cube(width, depth / colors.size(), height); color_part.translate({ 0.f, depth * i / colors.size(), 0. }); - v.iva_per_colors[i].load_mesh(color_part); - v.iva_per_colors[i].finalize_geometry(opengl_initialized); + v.model_per_colors[i].init_from(color_part); } - v.indexed_vertex_array.load_mesh(wipe_tower_shell); - v.indexed_vertex_array.finalize_geometry(opengl_initialized); + v.model.init_from(wipe_tower_shell); + v.mesh_raycaster = std::make_unique(std::make_shared(wipe_tower_shell)); v.set_convex_hull(wipe_tower_shell); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); @@ -1208,21 +763,19 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(volumes.size() - 1); } -GLVolume* GLVolumeCollection::new_toolpath_volume(const std::array& rgba, size_t reserve_vbo_floats) +GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba) { - GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats); - out->is_extrusion_path = true; - return out; + GLVolume* out = new_nontoolpath_volume(rgba); + out->is_extrusion_path = true; + return out; } -GLVolume* GLVolumeCollection::new_nontoolpath_volume(const std::array& rgba, size_t reserve_vbo_floats) +GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba) { - GLVolume *out = new GLVolume(rgba); - out->is_extrusion_path = false; - // Reserving number of vertices (3x position + 3x color) - out->indexed_vertex_array.reserve(reserve_vbo_floats / 6); - this->volumes.emplace_back(out); - return out; + GLVolume* out = new GLVolume(rgba); + out->is_extrusion_path = false; + this->volumes.emplace_back(out); + return out; } GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func) @@ -1232,7 +785,7 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i) { GLVolume* volume = volumes[i]; - bool is_transparent = (volume->render_color[3] < 1.0f); + bool is_transparent = volume->render_color.is_transparent(); auto tempGlwipeTowerVolume = dynamic_cast(volume); if (tempGlwipeTowerVolume) { is_transparent = tempGlwipeTowerVolume->IsTransparent(); @@ -1271,8 +824,8 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_suppo } //BBS: add outline drawing logic -void GLVolumeCollection::render( - GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d &view_matrix, std::function filter_func, bool with_outline) const +void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, + std::function filter_func, bool with_outline) const { GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); if (to_render.empty()) @@ -1282,6 +835,9 @@ void GLVolumeCollection::render( if (shader == nullptr) return; + GLShaderProgram* sink_shader = GUI::wxGetApp().get_shader("flat"); + GLShaderProgram* edges_shader = GUI::wxGetApp().get_shader("flat"); + if (type == ERenderType::Transparent) { glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -1307,20 +863,27 @@ void GLVolumeCollection::render( #endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT // render sinking contours of non-hovered volumes - if (m_show_sinking_contours) - if (volume.first->is_sinking() && !volume.first->is_below_printbed() && - volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) { - shader->stop_using(); - volume.first->render_sinking_contours(); - shader->start_using(); + shader->stop_using(); + if (sink_shader != nullptr) { + sink_shader->start_using(); + if (m_show_sinking_contours) { + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && + volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) { + volume.first->render_sinking_contours(); + } } + sink_shader->stop_using(); + } + shader->start_using(); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - - shader->set_uniform("uniform_color", volume.first->render_color); - shader->set_uniform("z_range", m_z_range, 2); - shader->set_uniform("clipping_plane", m_clipping_plane, 4); + if (!volume.first->model.is_initialized()) + shader->set_uniform("uniform_color", volume.first->render_color); + shader->set_uniform("z_range", m_z_range); + shader->set_uniform("clipping_plane", m_clipping_plane); + shader->set_uniform("use_color_clip_plane", m_use_color_clip_plane); + shader->set_uniform("color_clip_plane", m_color_clip_plane); + shader->set_uniform("uniform_color_clip_plane_1", m_color_clip_plane_colors[0]); + shader->set_uniform("uniform_color_clip_plane_2", m_color_clip_plane_colors[1]); //BOOST_LOG_TRIVIAL(info) << boost::format("set uniform_color to {%1%, %2%, %3%, %4%}, with_outline=%5%, selected %6%") // %volume.first->render_color[0]%volume.first->render_color[1]%volume.first->render_color[2]%volume.first->render_color[3] // %with_outline%volume.first->selected; @@ -1360,8 +923,17 @@ void GLVolumeCollection::render( #endif // ENABLE_ENVIRONMENT_MAP glcheck(); - //BBS: add outline related logic - volume.first->render(with_outline && volume.first->selected); + volume.first->model.set_color(volume.first->render_color); + const Transform3d model_matrix = volume.first->world_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + //BBS: add outline related logic + if (with_outline && volume.first->selected) + volume.first->render_with_outline(view_matrix * model_matrix); + else + volume.first->render(); #if ENABLE_ENVIRONMENT_MAP if (use_environment_texture) @@ -1370,23 +942,24 @@ void GLVolumeCollection::render( glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); } if (m_show_sinking_contours) { - for (GLVolumeWithIdAndZ& volume : to_render) { - // render sinking contours of hovered/displaced volumes - if (volume.first->is_sinking() && !volume.first->is_below_printbed() && - (volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) { - shader->stop_using(); - glsafe(::glDepthFunc(GL_ALWAYS)); - volume.first->render_sinking_contours(); - glsafe(::glDepthFunc(GL_LESS)); - shader->start_using(); + shader->stop_using(); + if (sink_shader != nullptr) { + sink_shader->start_using(); + for (GLVolumeWithIdAndZ& volume : to_render) { + // render sinking contours of hovered/displaced volumes + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && + (volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) { + glsafe(::glDepthFunc(GL_ALWAYS)); + volume.first->render_sinking_contours(); + glsafe(::glDepthFunc(GL_LESS)); + } } + sink_shader->start_using(); } + shader->start_using(); } if (disable_cullface) @@ -1539,83 +1112,53 @@ void GLVolumeCollection::reset_outside_state() void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha) { - static const float inv_255 = 1.0f / 255.0f; + + using ColorItem = std::pair; + std::vector colors; - struct Color - { - std::string text; - unsigned char rgba[4]; - - Color() - : text("") - { - rgba[0] = 255; - rgba[1] = 255; - rgba[2] = 255; - rgba[3] = 255; - } - - void set(const std::string& text, unsigned char* rgba) - { - this->text = text; - ::memcpy((void*)this->rgba, (const void*)rgba, 4 * sizeof(unsigned char)); - } - }; - - if (config == nullptr) - return; - - unsigned char rgba[4]; - std::vector colors; - - if (static_cast(config->opt_int("printer_technology")) == ptSLA) - { + if (static_cast(config->opt_int("printer_technology")) == ptSLA) { const std::string& txt_color = config->opt_string("material_colour").empty() ? print_config_def.get("material_colour")->get_default_value()->value : config->opt_string("material_colour"); - if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba)) { - colors.resize(1); - colors[0].set(txt_color, rgba); - } + ColorRGBA rgba; + if (decode_color(txt_color, rgba)) + colors.push_back({ txt_color, rgba }); } - else - { + else { const ConfigOptionStrings* filamemts_opt = dynamic_cast(config->option("filament_colour")); if (filamemts_opt == nullptr) return; - unsigned int colors_count = (unsigned int)filamemts_opt->values.size(); + size_t colors_count = (size_t)filamemts_opt->values.size(); if (colors_count == 0) return; colors.resize(colors_count); for (unsigned int i = 0; i < colors_count; ++i) { - const std::string& txt_color = config->opt_string("filament_colour", i); - if (Slic3r::GUI::BitmapCache::parse_color4(txt_color, rgba)) - colors[i].set(txt_color, rgba); + ColorRGBA rgba; + const std::string& fil_color = config->opt_string("filament_colour", i); + if (decode_color(fil_color, rgba)) + colors[i] = { fil_color, rgba }; } } for (GLVolume* volume : volumes) { - if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0)) + if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0) continue; int extruder_id = volume->extruder_id - 1; if (extruder_id < 0 || (int)colors.size() <= extruder_id) extruder_id = 0; - const Color& color = colors[extruder_id]; - if (!color.text.empty()) { - for (int i = 0; i < 4; ++i) { - if (is_update_alpha == false) { - if (i < 3) { - volume->color[i] = (float) color.rgba[i] * inv_255; - } - continue; - } - volume->color[i] = (float) color.rgba[i] * inv_255; - } - } + const ColorItem& color = colors[extruder_id]; + if (!color.first.empty()) { + if (!is_update_alpha) { + float old_a = color.second.a(); + volume->color = color.second; + volume->color.a(old_a); + } + volume->color = color.second; + } } } @@ -1625,7 +1168,7 @@ void GLVolumeCollection::set_transparency(float alpha) if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0)) continue; - volume->color[3] = alpha; + volume->color.a(alpha); } } @@ -1677,60 +1220,63 @@ std::string GLVolumeCollection::log_memory_info() const return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; } -// caller is responsible for supplying NO lines with zero length -static void thick_lines_to_indexed_vertex_array( - const Lines &lines, - const std::vector &widths, - const std::vector &heights, - bool closed, - double top_z, - GLIndexedVertexArray &volume) +static void thick_lines_to_geometry( + const Lines& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) { - assert(! lines.empty()); + assert(!lines.empty()); if (lines.empty()) return; -#define LEFT 0 -#define RIGHT 1 -#define TOP 2 -#define BOTTOM 3 + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; // right, left, top, bottom - int idx_prev[4] = { -1, -1, -1, -1 }; - double bottom_z_prev = 0.; - Vec2d b1_prev(Vec2d::Zero()); - Vec2d v_prev(Vec2d::Zero()); - int idx_initial[4] = { -1, -1, -1, -1 }; - double width_initial = 0.; - double bottom_z_initial = 0.0; - double len_prev = 0.0; + std::array idx_prev = { -1, -1, -1, -1 }; + std::array idx_initial = { -1, -1, -1, -1 }; + + double bottom_z_prev = 0.0; + Vec2d b1_prev(Vec2d::Zero()); + Vec2d v_prev(Vec2d::Zero()); + double len_prev = 0.0; + double width_initial = 0.0; + double bottom_z_initial = 0.0; // loop once more in case of closed loops - size_t lines_end = closed ? (lines.size() + 1) : lines.size(); - for (size_t ii = 0; ii < lines_end; ++ ii) { - size_t i = (ii == lines.size()) ? 0 : ii; - const Line &line = lines[i]; - double bottom_z = top_z - heights[i]; - double middle_z = 0.5 * (top_z + bottom_z); - double width = widths[i]; + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; + const Line& line = lines[i]; + const double bottom_z = top_z - heights[i]; + const double middle_z = 0.5 * (top_z + bottom_z); + const double width = widths[i]; - bool is_first = (ii == 0); - bool is_last = (ii == lines_end - 1); - bool is_closing = closed && is_last; + const bool is_first = (ii == 0); + const bool is_last = (ii == lines_end - 1); + const bool is_closing = closed && is_last; - Vec2d v = unscale(line.vector()).normalized(); - double len = unscale(line.length()); + const Vec2d v = unscale(line.vector()).normalized(); + const double len = unscale(line.length()); - Vec2d a = unscale(line.a); - Vec2d b = unscale(line.b); + const Vec2d a = unscale(line.a); + const Vec2d b = unscale(line.b); Vec2d a1 = a; Vec2d a2 = a; Vec2d b1 = b; Vec2d b2 = b; { - double dist = 0.5 * width; // scaled - double dx = dist * v(0); - double dy = dist * v(1); + const double dist = 0.5 * width; // scaled + const double dx = dist * v.x(); + const double dy = dist * v.y(); a1 += Vec2d(+dy, -dx); a2 += Vec2d(-dy, +dx); b1 += Vec2d(+dy, -dx); @@ -1738,102 +1284,101 @@ static void thick_lines_to_indexed_vertex_array( } // calculate new XY normals - Vec2d xy_right_normal = unscale(line.normal()).normalized(); + const Vec2d xy_right_normal = unscale(line.normal()).normalized(); - int idx_a[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings - int idx_b[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); + std::array idx_a = { 0, 0, 0, 0 }; + std::array idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); - bool bottom_z_different = bottom_z_prev != bottom_z; + const bool bottom_z_different = bottom_z_prev != bottom_z; bottom_z_prev = bottom_z; - if (!is_first && bottom_z_different) - { + if (!is_first && bottom_z_different) { // Found a change of the layer thickness -> Add a cap at the end of the previous segment. - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); + geometry.add_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); } // Share top / bottom vertices if possible. if (is_first) { - idx_a[TOP] = idx_last++; - volume.push_geometry(a(0), a(1), top_z , 0., 0., 1.); - } else { - idx_a[TOP] = idx_prev[TOP]; + idx_a[Top] = idx_last++; + geometry.add_vertex(Vec3f(a.x(), a.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); } + else + idx_a[Top] = idx_prev[Top]; if (is_first || bottom_z_different) { // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. - idx_a[BOTTOM] = idx_last ++; - volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.); - idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0); - idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0); - } - else { - idx_a[BOTTOM] = idx_prev[BOTTOM]; + idx_a[Bottom] = idx_last++; + geometry.add_vertex(Vec3f(a.x(), a.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_a[Right] = idx_last++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); } + else + idx_a[Bottom] = idx_prev[Bottom]; if (is_first) { // Start of the 1st line segment. - width_initial = width; + width_initial = width; bottom_z_initial = bottom_z; - memcpy(idx_initial, idx_a, sizeof(int) * 4); - } else { + idx_initial = idx_a; + } + else { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = v_prev.dot(v); + const double v_dot = v_prev.dot(v); // To reduce gpu memory usage, we try to reuse vertices - // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges // is longer than a fixed threshold. // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts - double len_threshold = 2.5; + const double len_threshold = 2.5; // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met - bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold); + const bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold); if (sharp) { - if (!bottom_z_different) - { + if (!bottom_z_different) { // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0); - if (cross2(v_prev, v) > 0.) { + idx_a[Right] = idx_last++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + if (cross2(v_prev, v) > 0.0) { // Right turn. Fill in the right turn wedge. - volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]); - volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]); + geometry.add_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); } else { // Left turn. Fill in the left turn wedge. - volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]); - volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]); + geometry.add_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); } } } - else - { - if (!bottom_z_different) - { + else { + if (!bottom_z_different) { // The two successive segments are nearly collinear. - idx_a[LEFT ] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; } } if (is_closing) { if (!sharp) { - if (!bottom_z_different) - { + if (!bottom_z_different) { // Closing a loop with smooth transition. Unify the closing left / right vertices. - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6); - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); - volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); // Replace the left / right vertex indices to point to the start of the loop. - for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) { - if (volume.quad_indices[u] == idx_prev[LEFT]) - volume.quad_indices[u] = idx_initial[LEFT]; - else if (volume.quad_indices[u] == idx_prev[RIGHT]) - volume.quad_indices[u] = idx_initial[RIGHT]; + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_index(u, (unsigned int)idx_initial[Right]); } } } @@ -1843,235 +1388,232 @@ static void thick_lines_to_indexed_vertex_array( } // Only new allocate top / bottom vertices, if not closing a loop. - if (is_closing) { - idx_b[TOP] = idx_initial[TOP]; - } else { - idx_b[TOP] = idx_last ++; - volume.push_geometry(b(0), b(1), top_z , 0., 0., 1.); + if (is_closing) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); } - if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) { - idx_b[BOTTOM] = idx_initial[BOTTOM]; - } else { - idx_b[BOTTOM] = idx_last ++; - volume.push_geometry(b(0), b(1), bottom_z, 0., 0., -1.); + if (is_closing && width == width_initial && bottom_z == bottom_z_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); } // Generate new vertices for the end of this line segment. - idx_b[LEFT ] = idx_last ++; - volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0); - idx_b[RIGHT ] = idx_last ++; - volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0); + idx_b[Left] = idx_last++; + geometry.add_vertex(Vec3f(b2.x(), b2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_b[Right] = idx_last++; + geometry.add_vertex(Vec3f(b1.x(), b1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); - memcpy(idx_prev, idx_b, 4 * sizeof(int)); + idx_prev = idx_b; bottom_z_prev = bottom_z; b1_prev = b1; v_prev = v; len_prev = len; - if (bottom_z_different && (closed || (!is_first && !is_last))) - { + if (bottom_z_different && (closed || (!is_first && !is_last))) { // Found a change of the layer thickness -> Add a cap at the beginning of this segment. - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); + geometry.add_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); } - if (! closed) { + if (!closed) { // Terminate open paths with caps. - if (is_first) - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); + if (is_first) { + geometry.add_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } // We don't use 'else' because both cases are true if we have only one line. - if (is_last) - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); + if (is_last) { + geometry.add_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } } // Add quads for a straight hollow tube-like segment. // bottom-right face - volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); + geometry.add_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); // top-right face - volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]); + geometry.add_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); // top-left face - volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]); + geometry.add_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); // bottom-left face - volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]); + geometry.add_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); } - -#undef LEFT -#undef RIGHT -#undef TOP -#undef BOTTOM } // caller is responsible for supplying NO lines with zero length -static void thick_lines_to_indexed_vertex_array(const Lines3& lines, +static void thick_lines_to_geometry( + const Lines3& lines, const std::vector& widths, const std::vector& heights, - bool closed, - GLIndexedVertexArray& volume) + bool closed, + GUI::GLModel::Geometry& geometry) { assert(!lines.empty()); if (lines.empty()) return; -#define LEFT 0 -#define RIGHT 1 -#define TOP 2 -#define BOTTOM 3 + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; // left, right, top, bottom - int idx_initial[4] = { -1, -1, -1, -1 }; - int idx_prev[4] = { -1, -1, -1, -1 }; - double z_prev = 0.0; - double len_prev = 0.0; - Vec3d n_right_prev = Vec3d::Zero(); - Vec3d n_top_prev = Vec3d::Zero(); - Vec3d unit_v_prev = Vec3d::Zero(); - double width_initial = 0.0; + std::array idx_prev = { -1, -1, -1, -1 }; + std::array idx_initial = { -1, -1, -1, -1 }; + + double z_prev = 0.0; + double len_prev = 0.0; + Vec3d n_right_prev = Vec3d::Zero(); + Vec3d n_top_prev = Vec3d::Zero(); + Vec3d unit_v_prev = Vec3d::Zero(); + double width_initial = 0.0; // new vertices around the line endpoints // left, right, top, bottom - Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; - Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + std::array a = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + std::array b = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; // loop once more in case of closed loops - size_t lines_end = closed ? (lines.size() + 1) : lines.size(); - for (size_t ii = 0; ii < lines_end; ++ii) - { - size_t i = (ii == lines.size()) ? 0 : ii; + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; const Line3& line = lines[i]; - double height = heights[i]; - double width = widths[i]; + const double height = heights[i]; + const double width = widths[i]; - Vec3d unit_v = unscale(line.vector()).normalized(); - double len = unscale(line.length()); + const Vec3d unit_v = unscale(line.vector()).normalized(); + const double len = unscale(line.length()); Vec3d n_top = Vec3d::Zero(); Vec3d n_right = Vec3d::Zero(); - if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1))) - { + if (line.a.x() == line.b.x() && line.a.y() == line.b.y()) { // vertical segment n_top = Vec3d::UnitY(); n_right = Vec3d::UnitX(); - if (line.a(2) < line.b(2)) + if (line.a.z() < line.b.z()) n_right = -n_right; } - else - { + else { // horizontal segment n_right = unit_v.cross(Vec3d::UnitZ()).normalized(); n_top = n_right.cross(unit_v).normalized(); } - Vec3d rl_displacement = 0.5 * width * n_right; - Vec3d tb_displacement = 0.5 * height * n_top; - Vec3d l_a = unscale(line.a); - Vec3d l_b = unscale(line.b); + const Vec3d rl_displacement = 0.5 * width * n_right; + const Vec3d tb_displacement = 0.5 * height * n_top; + const Vec3d l_a = unscale(line.a); + const Vec3d l_b = unscale(line.b); - a[RIGHT] = l_a + rl_displacement; - a[LEFT] = l_a - rl_displacement; - a[TOP] = l_a + tb_displacement; - a[BOTTOM] = l_a - tb_displacement; - b[RIGHT] = l_b + rl_displacement; - b[LEFT] = l_b - rl_displacement; - b[TOP] = l_b + tb_displacement; - b[BOTTOM] = l_b - tb_displacement; + a[Right] = l_a + rl_displacement; + a[Left] = l_a - rl_displacement; + a[Top] = l_a + tb_displacement; + a[Bottom] = l_a - tb_displacement; + b[Right] = l_b + rl_displacement; + b[Left] = l_b - rl_displacement; + b[Top] = l_b + tb_displacement; + b[Bottom] = l_b - tb_displacement; - Vec3d n_bottom = -n_top; - Vec3d n_left = -n_right; + const Vec3d n_bottom = -n_top; + const Vec3d n_left = -n_right; - int idx_a[4]; - int idx_b[4]; - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); + std::array idx_a = { 0, 0, 0, 0}; + std::array idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); - bool z_different = (z_prev != l_a(2)); - z_prev = l_b(2); + const bool z_different = (z_prev != l_a.z()); + z_prev = l_b.z(); // Share top / bottom vertices if possible. - if (ii == 0) - { - idx_a[TOP] = idx_last++; - volume.push_geometry(a[TOP], n_top); + if (ii == 0) { + idx_a[Top] = idx_last++; + geometry.add_vertex((Vec3f)a[Top].cast(), (Vec3f)n_top.cast()); } else - idx_a[TOP] = idx_prev[TOP]; + idx_a[Top] = idx_prev[Top]; - if ((ii == 0) || z_different) - { + if (ii == 0 || z_different) { // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. - idx_a[BOTTOM] = idx_last++; - volume.push_geometry(a[BOTTOM], n_bottom); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a[LEFT], n_left); - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a[RIGHT], n_right); + idx_a[Bottom] = idx_last++; + geometry.add_vertex((Vec3f)a[Bottom].cast(), (Vec3f)n_bottom.cast()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast(), (Vec3f)n_left.cast()); + idx_a[Right] = idx_last++; + geometry.add_vertex((Vec3f)a[Right].cast(), (Vec3f)n_right.cast()); } else - idx_a[BOTTOM] = idx_prev[BOTTOM]; + idx_a[Bottom] = idx_prev[Bottom]; - if (ii == 0) - { + if (ii == 0) { // Start of the 1st line segment. width_initial = width; - ::memcpy(idx_initial, idx_a, sizeof(int) * 4); + idx_initial = idx_a; } - else - { + else { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = unit_v_prev.dot(unit_v); - bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; + const double v_dot = unit_v_prev.dot(unit_v); + const bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; // To reduce gpu memory usage, we try to reuse vertices - // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges // is longer than a fixed threshold. // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts - double len_threshold = 2.5; + const double len_threshold = 2.5; // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met - bool is_sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold); - if (is_sharp) - { + const bool is_sharp = v_dot < 0.707 || len_prev > len_threshold || len > len_threshold; + if (is_sharp) { // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a[RIGHT], n_right); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a[LEFT], n_left); + idx_a[Right] = idx_last++; + geometry.add_vertex((Vec3f)a[Right].cast(), (Vec3f)n_right.cast()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast(), (Vec3f)n_left.cast()); - if (is_right_turn) - { + if (is_right_turn) { // Right turn. Fill in the right turn wedge. - volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]); - volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]); + geometry.add_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); } - else - { + else { // Left turn. Fill in the left turn wedge. - volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]); - volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]); + geometry.add_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); } } - else - { + else { // The two successive segments are nearly collinear. - idx_a[LEFT] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; } - if (ii == lines.size()) - { - if (!is_sharp) - { + if (ii == lines.size()) { + if (!is_sharp) { // Closing a loop with smooth transition. Unify the closing left / right vertices. - ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6, sizeof(float) * 6); - ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); - volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); // Replace the left / right vertex indices to point to the start of the loop. - for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++u) - { - if (volume.quad_indices[u] == idx_prev[LEFT]) - volume.quad_indices[u] = idx_initial[LEFT]; - else if (volume.quad_indices[u] == idx_prev[RIGHT]) - volume.quad_indices[u] = idx_initial[RIGHT]; + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_index(u, (unsigned int)idx_initial[Right]); } } @@ -2081,246 +1623,161 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, } // Only new allocate top / bottom vertices, if not closing a loop. - if (closed && (ii + 1 == lines.size())) - idx_b[TOP] = idx_initial[TOP]; - else - { - idx_b[TOP] = idx_last++; - volume.push_geometry(b[TOP], n_top); + if (closed && ii + 1 == lines.size()) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex((Vec3f)b[Top].cast(), (Vec3f)n_top.cast()); } - if (closed && (ii + 1 == lines.size()) && (width == width_initial)) - idx_b[BOTTOM] = idx_initial[BOTTOM]; - else - { - idx_b[BOTTOM] = idx_last++; - volume.push_geometry(b[BOTTOM], n_bottom); + if (closed && ii + 1 == lines.size() && width == width_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex((Vec3f)b[Bottom].cast(), (Vec3f)n_bottom.cast()); } // Generate new vertices for the end of this line segment. - idx_b[LEFT] = idx_last++; - volume.push_geometry(b[LEFT], n_left); - idx_b[RIGHT] = idx_last++; - volume.push_geometry(b[RIGHT], n_right); + idx_b[Left] = idx_last++; + geometry.add_vertex((Vec3f)b[Left].cast(), (Vec3f)n_left.cast()); + idx_b[Right] = idx_last++; + geometry.add_vertex((Vec3f)b[Right].cast(), (Vec3f)n_right.cast()); - ::memcpy(idx_prev, idx_b, 4 * sizeof(int)); + idx_prev = idx_b; n_right_prev = n_right; n_top_prev = n_top; unit_v_prev = unit_v; len_prev = len; - if (!closed) - { + if (!closed) { // Terminate open paths with caps. - if (i == 0) - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); + if (i == 0) { + geometry.add_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } // We don't use 'else' because both cases are true if we have only one line. - if (i + 1 == lines.size()) - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); + if (i + 1 == lines.size()) { + geometry.add_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } } // Add quads for a straight hollow tube-like segment. // bottom-right face - volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); + geometry.add_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); // top-right face - volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]); + geometry.add_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); // top-left face - volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]); + geometry.add_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); // bottom-left face - volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]); + geometry.add_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); } - -#undef LEFT -#undef RIGHT -#undef TOP -#undef BOTTOM -} - -static void point_to_indexed_vertex_array(const Vec3crd& point, - double width, - double height, - GLIndexedVertexArray& volume) -{ - // builds a double piramid, with vertices on the local axes, around the point - - Vec3d center = unscale(point); - - double scale_factor = 1.0; - double w = scale_factor * width; - double h = scale_factor * height; - - // new vertices ids - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - int idxs[6]; - for (int i = 0; i < 6; ++i) - { - idxs[i] = idx_last + i; - } - - Vec3d displacement_x(w, 0.0, 0.0); - Vec3d displacement_y(0.0, w, 0.0); - Vec3d displacement_z(0.0, 0.0, h); - - Vec3d unit_x(1.0, 0.0, 0.0); - Vec3d unit_y(0.0, 1.0, 0.0); - Vec3d unit_z(0.0, 0.0, 1.0); - - // vertices - volume.push_geometry(center - displacement_x, -unit_x); // idxs[0] - volume.push_geometry(center + displacement_x, unit_x); // idxs[1] - volume.push_geometry(center - displacement_y, -unit_y); // idxs[2] - volume.push_geometry(center + displacement_y, unit_y); // idxs[3] - volume.push_geometry(center - displacement_z, -unit_z); // idxs[4] - volume.push_geometry(center + displacement_z, unit_z); // idxs[5] - - // top piramid faces - volume.push_triangle(idxs[0], idxs[2], idxs[5]); - volume.push_triangle(idxs[2], idxs[1], idxs[5]); - volume.push_triangle(idxs[1], idxs[3], idxs[5]); - volume.push_triangle(idxs[3], idxs[0], idxs[5]); - - // bottom piramid faces - volume.push_triangle(idxs[2], idxs[0], idxs[4]); - volume.push_triangle(idxs[1], idxs[2], idxs[4]); - volume.push_triangle(idxs[3], idxs[1], idxs[4]); - volume.push_triangle(idxs[0], idxs[3], idxs[4]); } void _3DScene::thick_lines_to_verts( - const Lines &lines, - const std::vector &widths, - const std::vector &heights, - bool closed, - double top_z, - GLVolume &volume) -{ - thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); -} - -void _3DScene::thick_lines_to_verts(const Lines3& lines, + const Lines& lines, const std::vector& widths, const std::vector& heights, - bool closed, - GLVolume& volume) + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) { - thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array); + thick_lines_to_geometry(lines, widths, heights, closed, top_z, geometry); } -static void thick_point_to_verts(const Vec3crd& point, - double width, - double height, - GLVolume& volume) +void _3DScene::thick_lines_to_verts( + const Lines3& lines, + const std::vector& widths, + const std::vector& heights, + bool closed, + GUI::GLModel::Geometry& geometry) { - point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array); -} - -void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume) -{ - if (polyline.size() >= 2) { - size_t num_segments = polyline.size() - 1; - thick_lines_to_verts(polyline.lines(), std::vector(num_segments, width), std::vector(num_segments, height), false, print_z, volume); - } + thick_lines_to_geometry(lines, widths, heights, closed, geometry); } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) -{ - extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); -} - -// Fill in the qverts and tverts with quads and triangles for the extrusion_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); polyline.translate(copy); - Lines lines = polyline.lines(); + const Lines lines = polyline.lines(); std::vector widths(lines.size(), extrusion_path.width); std::vector heights(lines.size(), extrusion_path.height); - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. -void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { Lines lines; std::vector widths; std::vector heights; - for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) { + for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); polyline.translate(copy); - Lines lines_this = polyline.lines(); + const Lines lines_this = polyline.lines(); append(lines, lines_this); widths.insert(widths.end(), lines_this.size(), extrusion_path.width); heights.insert(heights.end(), lines_this.size(), extrusion_path.height); } - thick_lines_to_verts(lines, widths, heights, true, print_z, volume); + thick_lines_to_verts(lines, widths, heights, true, print_z, geometry); } // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { Lines lines; std::vector widths; std::vector heights; - for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) { + for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); polyline.translate(copy); - Lines lines_this = polyline.lines(); + const Lines lines_this = polyline.lines(); append(lines, lines_this); widths.insert(widths.end(), lines_this.size(), extrusion_path.width); heights.insert(heights.end(), lines_this.size(), extrusion_path.height); } - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } -void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { - for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) - extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); + for (const ExtrusionEntity* extrusion_entity : extrusion_entity_collection.entities) + extrusionentity_to_verts(extrusion_entity, print_z, copy, geometry); } -void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { if (extrusion_entity != nullptr) { - auto *extrusion_path = dynamic_cast(extrusion_entity); + auto* extrusion_path = dynamic_cast(extrusion_entity); if (extrusion_path != nullptr) - extrusionentity_to_verts(*extrusion_path, print_z, copy, volume); + extrusionentity_to_verts(*extrusion_path, print_z, copy, geometry); else { - auto *extrusion_loop = dynamic_cast(extrusion_entity); + auto* extrusion_loop = dynamic_cast(extrusion_entity); if (extrusion_loop != nullptr) - extrusionentity_to_verts(*extrusion_loop, print_z, copy, volume); + extrusionentity_to_verts(*extrusion_loop, print_z, copy, geometry); else { - auto *extrusion_multi_path = dynamic_cast(extrusion_entity); + auto* extrusion_multi_path = dynamic_cast(extrusion_entity); if (extrusion_multi_path != nullptr) - extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, volume); + extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, geometry); else { - auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); + auto* extrusion_entity_collection = dynamic_cast(extrusion_entity); if (extrusion_entity_collection != nullptr) - extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); - else { + extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, geometry); + else throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); - } } } } } } -void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) -{ - Lines3 lines = polyline.lines(); - std::vector widths(lines.size(), width); - std::vector heights(lines.size(), height); - thick_lines_to_verts(lines, widths, heights, false, volume); -} - -void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) -{ - thick_point_to_verts(point, width, height, volume); -} - } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index c850884da8..ce10a463f2 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -1,3 +1,14 @@ +///|/ Copyright (c) Prusa Research 2017 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2017 Eyal Soha @eyal0 +///|/ Copyright (c) Slic3r 2015 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/GUI/3DScene.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2013 Guillaume Seguin @iXce +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_3DScene_hpp_ #define slic3r_3DScene_hpp_ @@ -7,11 +18,13 @@ #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/Color.hpp" // BBS #include "libslic3r/ObjectID.hpp" #include "GLModel.hpp" #include "GLShader.hpp" +#include "MeshUtils.hpp" #include #include @@ -30,10 +43,10 @@ #define glsafe(cmd) cmd #define glcheck() #endif // HAS_GLSAFE -extern std::vector> get_extruders_colors(); -extern float FullyTransparentMaterialThreshold; -extern float FullTransparentModdifiedToFixAlpha; -extern std::array adjust_color_for_rendering(const std::array &colors); +extern std::vector get_extruders_colors(); +extern float FullyTransparentMaterialThreshold; +extern float FullTransparentModdifiedToFixAlpha; +extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors); namespace Slic3r { @@ -54,224 +67,23 @@ enum ModelInstanceEPrintVolumeState : unsigned char; using ModelObjectPtrs = std::vector; // Return appropriate color based on the ModelVolume. -std::array color_from_model_volume(const ModelVolume& model_volume); - -// A container for interleaved arrays of 3D vertices and normals, -// possibly indexed by triangles and / or quads. -class GLIndexedVertexArray { -public: - // Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized. - static_assert(sizeof(Eigen::AlignedBox) == 24, "Eigen::AlignedBox is not being vectorized, thus it does not need to be aligned"); - using BoundingBox = Eigen::AlignedBox; - - GLIndexedVertexArray() { m_bounding_box.setEmpty(); } - GLIndexedVertexArray(const GLIndexedVertexArray &rhs) : - vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved), - triangle_indices(rhs.triangle_indices), - quad_indices(rhs.quad_indices), - m_bounding_box(rhs.m_bounding_box) - { assert(!rhs.has_VBOs()); m_bounding_box.setEmpty(); } - GLIndexedVertexArray(GLIndexedVertexArray &&rhs) : - vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)), - triangle_indices(std::move(rhs.triangle_indices)), - quad_indices(std::move(rhs.quad_indices)), - m_bounding_box(rhs.m_bounding_box) - { assert(! rhs.has_VBOs()); } - - ~GLIndexedVertexArray() { release_geometry(); } - - GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs) - { - assert(vertices_and_normals_interleaved_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - assert(quad_indices_VBO_id == 0); - assert(rhs.vertices_and_normals_interleaved_VBO_id == 0); - assert(rhs.triangle_indices_VBO_id == 0); - assert(rhs.quad_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved; - this->triangle_indices = rhs.triangle_indices; - this->quad_indices = rhs.quad_indices; - this->m_bounding_box = rhs.m_bounding_box; - this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; - this->triangle_indices_size = rhs.triangle_indices_size; - this->quad_indices_size = rhs.quad_indices_size; - return *this; - } - - GLIndexedVertexArray& operator=(GLIndexedVertexArray &&rhs) - { - assert(vertices_and_normals_interleaved_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - assert(quad_indices_VBO_id == 0); - assert(rhs.vertices_and_normals_interleaved_VBO_id == 0); - assert(rhs.triangle_indices_VBO_id == 0); - assert(rhs.quad_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved); - this->triangle_indices = std::move(rhs.triangle_indices); - this->quad_indices = std::move(rhs.quad_indices); - this->m_bounding_box = rhs.m_bounding_box; - this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size; - this->triangle_indices_size = rhs.triangle_indices_size; - this->quad_indices_size = rhs.quad_indices_size; - return *this; - } - - // Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x) - std::vector vertices_and_normals_interleaved; - std::vector triangle_indices; - std::vector quad_indices; - - // When the geometry data is loaded into the graphics card as Vertex Buffer Objects, - // the above mentioned std::vectors are cleared and the following variables keep their original length. - size_t vertices_and_normals_interleaved_size{ 0 }; - size_t triangle_indices_size{ 0 }; - size_t quad_indices_size{ 0 }; - - // IDs of the Vertex Array Objects, into which the geometry has been loaded. - // Zero if the VBOs are not sent to GPU yet. - unsigned int vertices_and_normals_interleaved_VBO_id{ 0 }; - unsigned int triangle_indices_VBO_id{ 0 }; - unsigned int quad_indices_VBO_id{ 0 }; - -#if ENABLE_SMOOTH_NORMALS - void load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals = false); - void load_mesh(const TriangleMesh& mesh, bool smooth_normals = false) { this->load_mesh_full_shading(mesh, smooth_normals); } -#else - void load_mesh_full_shading(const TriangleMesh& mesh); - void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); } -#endif // ENABLE_SMOOTH_NORMALS - - void load_its_flat_shading(const indexed_triangle_set &its); - - inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } - - inline void reserve(size_t sz) { - this->vertices_and_normals_interleaved.reserve(sz * 6); - this->triangle_indices.reserve(sz * 3); - this->quad_indices.reserve(sz * 4); - } - - inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) { - assert(this->vertices_and_normals_interleaved_VBO_id == 0); - if (this->vertices_and_normals_interleaved_VBO_id != 0) - return; - - if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) - this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); - this->vertices_and_normals_interleaved.emplace_back(nx); - this->vertices_and_normals_interleaved.emplace_back(ny); - this->vertices_and_normals_interleaved.emplace_back(nz); - this->vertices_and_normals_interleaved.emplace_back(x); - this->vertices_and_normals_interleaved.emplace_back(y); - this->vertices_and_normals_interleaved.emplace_back(z); - - this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); - m_bounding_box.extend(Vec3f(x, y, z)); - }; - - inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) { - push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); - } - - template - inline void push_geometry(const Eigen::MatrixBase& p, const Eigen::MatrixBase& n) { - push_geometry(float(p(0)), float(p(1)), float(p(2)), float(n(0)), float(n(1)), float(n(2))); - } - - inline void push_triangle(int idx1, int idx2, int idx3) { - assert(this->vertices_and_normals_interleaved_VBO_id == 0); - if (this->vertices_and_normals_interleaved_VBO_id != 0) - return; - - if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) - this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); - this->triangle_indices.emplace_back(idx1); - this->triangle_indices.emplace_back(idx2); - this->triangle_indices.emplace_back(idx3); - this->triangle_indices_size = this->triangle_indices.size(); - }; - - inline void push_quad(int idx1, int idx2, int idx3, int idx4) { - assert(this->vertices_and_normals_interleaved_VBO_id == 0); - if (this->vertices_and_normals_interleaved_VBO_id != 0) - return; - - if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity()) - this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4)); - this->quad_indices.emplace_back(idx1); - this->quad_indices.emplace_back(idx2); - this->quad_indices.emplace_back(idx3); - this->quad_indices.emplace_back(idx4); - this->quad_indices_size = this->quad_indices.size(); - }; - - // Finalize the initialization of the geometry & indices, - // upload the geometry and indices to OpenGL VBO objects - // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry(bool opengl_initialized); - // Release the geometry data, release OpenGL VBOs. - void release_geometry(); - - void render() const; - void render(const std::pair& tverts_range, const std::pair& qverts_range) const; - - // Is there any geometry data stored? - bool empty() const { return vertices_and_normals_interleaved_size == 0; } - - void clear() { - this->vertices_and_normals_interleaved.clear(); - this->triangle_indices.clear(); - this->quad_indices.clear(); - vertices_and_normals_interleaved_size = 0; - triangle_indices_size = 0; - quad_indices_size = 0; - m_bounding_box.setEmpty(); - } - - // Shrink the internal storage to tighly fit the data stored. - void shrink_to_fit() { - this->vertices_and_normals_interleaved.shrink_to_fit(); - this->triangle_indices.shrink_to_fit(); - this->quad_indices.shrink_to_fit(); - } - - const BoundingBox& bounding_box() const { return m_bounding_box; } - - // Return an estimate of the memory consumed by this class. - size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); } - // Return an estimate of the memory held by GPU vertex buffers. - size_t gpu_memory_used() const - { - size_t memsize = 0; - if (this->vertices_and_normals_interleaved_VBO_id != 0) - memsize += this->vertices_and_normals_interleaved_size * 4; - if (this->triangle_indices_VBO_id != 0) - memsize += this->triangle_indices_size * 4; - if (this->quad_indices_VBO_id != 0) - memsize += this->quad_indices_size * 4; - return memsize; - } - size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } - -private: - BoundingBox m_bounding_box; -}; +extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); class GLVolume { public: std::string name; - static std::array DISABLED_COLOR; - static std::array SLA_SUPPORT_COLOR; - static std::array SLA_PAD_COLOR; - static std::array NEUTRAL_COLOR; - static std::array UNPRINTABLE_COLOR; - static std::array, 5> MODEL_COLOR; - static std::array MODEL_MIDIFIER_COL; - static std::array MODEL_NEGTIVE_COL; - static std::array SUPPORT_ENFORCER_COL; - static std::array SUPPORT_BLOCKER_COL; - static std::array MODEL_HIDDEN_COL; + static ColorRGBA DISABLED_COLOR; + static ColorRGBA SLA_SUPPORT_COLOR; + static ColorRGBA SLA_PAD_COLOR; + static ColorRGBA NEUTRAL_COLOR; + static ColorRGBA UNPRINTABLE_COLOR; + static std::array MODEL_COLOR; + static ColorRGBA MODEL_MIDIFIER_COL; + static ColorRGBA MODEL_NEGTIVE_COL; + static ColorRGBA SUPPORT_ENFORCER_COL; + static ColorRGBA SUPPORT_BLOCKER_COL; + static ColorRGBA MODEL_HIDDEN_COL; static void update_render_colors(); static void load_render_colors(); @@ -288,7 +100,7 @@ public: }; GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); - GLVolume(const std::array& rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} + GLVolume(const ColorRGBA& color) : GLVolume(color.r(), color.g(), color.b(), color.a()) {} virtual ~GLVolume() = default; // BBS @@ -329,9 +141,9 @@ protected: public: // Color of the triangles / quads held by this volume. - std::array color; + ColorRGBA color; // Color used to render this volume. - std::array render_color; + ColorRGBA render_color; struct CompositeID { CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {} @@ -394,20 +206,22 @@ public: bool force_neutral_color : 1; // Whether or not to force rendering of sinking contours bool force_sinking_contours : 1; + // Is render for picking + bool picking : 1; }; // Is mouse or rectangle selection over this object to select/deselect it ? EHoverState hover; - // Interleaved triangles & normals with indexed triangles & quads. - GLIndexedVertexArray indexed_vertex_array; + GUI::GLModel model; + // raycaster used for picking + std::unique_ptr mesh_raycaster; // BBS - mutable std::vector mmuseg_ivas; + mutable std::vector mmuseg_models; mutable ObjectBase::Timestamp mmuseg_ts; // Ranges of triangle and quad indices to be rendered. std::pair tverts_range; - std::pair qverts_range; // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // of the extrusions per layer. @@ -417,18 +231,11 @@ public: // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 bounding_box() const { - BoundingBoxf3 out; - if (! this->indexed_vertex_array.bounding_box().isEmpty()) { - out.min = this->indexed_vertex_array.bounding_box().min().cast(); - out.max = this->indexed_vertex_array.bounding_box().max().cast(); - out.defined = true; - }; - return out; + return this->model.get_bounding_box(); } - void set_color(const std::array& rgba); - void set_render_color(float r, float g, float b, float a); - void set_render_color(const std::array& rgba); + void set_color(const ColorRGBA& rgba) { color = rgba; } + void set_render_color(const ColorRGBA& rgba) { render_color = rgba; } // Sets render color in dependence of current state void set_render_color(); // set color according to model volume @@ -517,18 +324,17 @@ public: // convex hull const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } - bool empty() const { return this->indexed_vertex_array.empty(); } + bool empty() const { return this->model.is_empty(); } void set_range(double low, double high); + virtual void render(); + //BBS: add outline related logic and add virtual specifier - virtual void render(bool with_outline = false) const; + virtual void render_with_outline(const Transform3d &view_model_matrix); //BBS: add simple render function for thumbnail - void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector>& extruder_colors) const; - - void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } - void release_geometry() { this->indexed_vertex_array.release_geometry(); } + void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector extruder_colors); void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); @@ -545,25 +351,26 @@ public: // Return an estimate of the memory consumed by this class. size_t cpu_memory_used() const { - //FIXME what to do wih m_convex_hull? - return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t); + return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + + this->offsets.capacity() * sizeof(size_t); } // Return an estimate of the memory held by GPU vertex buffers. - size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } + size_t gpu_memory_used() const { return this->model.gpu_memory_used(); } size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } }; // BBS class GLWipeTowerVolume : public GLVolume { public: - GLWipeTowerVolume(const std::vector>& colors); - virtual void render(bool with_outline = false) const; + GLWipeTowerVolume(const std::vector& colors); + void render() override; + void render_with_outline(const Transform3d &view_model_matrix) override { render(); } - std::vector iva_per_colors; + std::vector model_per_colors; bool IsTransparent(); private: - std::vector> m_colors; + std::vector m_colors; }; typedef std::vector GLVolumePtrs; @@ -599,10 +406,16 @@ private: PrintVolume m_render_volume; // z range for clipping in shaders - float m_z_range[2]; + std::array m_z_range; // plane coeffs for clipping in shaders - float m_clipping_plane[4]; + std::array m_clipping_plane; + + // plane coeffs for render volumes with different colors in shaders + // used by cut gizmo + std::array m_color_clip_plane; + bool m_use_color_clip_plane{ false }; + std::array m_color_clip_plane_colors{ ColorRGBA::RED(), ColorRGBA::BLUE() }; struct Slope { @@ -627,19 +440,15 @@ public: ~GLVolumeCollection() { clear(); } std::vector load_object( - const ModelObject *model_object, + const ModelObject* model_object, int obj_idx, - const std::vector &instance_idxs, - const std::string &color_by, - bool opengl_initialized); + const std::vector& instance_idxs); int load_object_volume( - const ModelObject *model_object, + const ModelObject* model_object, int obj_idx, int volume_idx, int instance_idx, - const std::string &color_by, - bool opengl_initialized, bool in_assemble_view = false, bool use_loaded_id = false); @@ -651,31 +460,20 @@ public: const std::vector>& instances, SLAPrintObjectStep milestone, // Timestamp of the last change of the milestone - size_t timestamp, - bool opengl_initialized); + size_t timestamp); int load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); - GLVolume* new_toolpath_volume(const std::array& rgba, size_t reserve_vbo_floats = 0); - GLVolume* new_nontoolpath_volume(const std::array& rgba, size_t reserve_vbo_floats = 0); + GLVolume* new_toolpath_volume(const ColorRGBA& rgba); + GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); int get_selection_support_threshold_angle(bool&) const; // Render the volumes by OpenGL. //BBS: add outline drawing logic - void render(ERenderType type, - bool disable_cullface, - const Transform3d & view_matrix, - std::function filter_func = std::function(), - bool with_outline = true) const; + void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, + std::function filter_func = std::function(), bool with_outline = true) const; - // Finalize the initialization of the geometry & indices, - // upload the geometry and indices to OpenGL VBO objects - // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); } - // Release the geometry data assigned to the volumes. - // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. - void release_geometry() { for (auto *v : volumes) v->release_geometry(); } // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } @@ -685,7 +483,18 @@ public: void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; } void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; } - void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; } + void set_clipping_plane(const std::array& coeffs) { m_clipping_plane = coeffs; } + + const std::array& get_z_range() const { return m_z_range; } + const std::array& get_clipping_plane() const { return m_clipping_plane; } + + void set_use_color_clip_plane(bool use) { m_use_color_clip_plane = use; } + void set_color_clip_plane(const Vec3d& cp_normal, double offset) { + for (int i = 0; i < 3; ++i) + m_color_clip_plane[i] = -cp_normal[i]; + m_color_clip_plane[3] = offset; + } + void set_color_clip_plane_colors(const std::array& colors) { m_color_clip_plane_colors = colors; } bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; } bool is_slope_active() const { return m_slope.active; } @@ -726,17 +535,13 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo struct _3DScene { - static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); - static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); - static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); - static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); - static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); + static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry); + static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); }; } diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index df344ba62f..8f3fe0f130 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -2,6 +2,7 @@ #include "I18N.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" @@ -127,10 +128,10 @@ wxString CopyrightsDialog::get_html_text() wxColour bgr_clr = wxGetApp().get_window_default_clr();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); const auto text_clr = wxGetApp().get_label_clr_default();// wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + const auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue())); + const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue())); - const wxString copyright_str = _(L("Copyright")) + "© "; + const wxString copyright_str = _L("Copyright") + "© "; wxString text = wxString::Format( "" diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 20201faf0f..912faf6bfb 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -18,6 +18,7 @@ namespace GUI { class ConfigOptionsGroup; using ConfigOptionsGroupShp = std::shared_ptr; +using ConfigOptionsGroupWkp = std::weak_ptr; struct BedShape { diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index cfd027dbc8..e765b1b9ff 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -673,47 +673,6 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); } -bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) -{ - if (scolor.size() == 9) { - unsigned char rgba[4]; - parse_color4(scolor, rgba); - rgb_out[0] = rgba[0]; - rgb_out[1] = rgba[1]; - rgb_out[2] = rgba[2]; - return true; - } - rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; - if (scolor.size() != 7 || scolor.front() != '#') - return false; - const char* c = scolor.data() + 1; - for (size_t i = 0; i < 3; ++i) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) - return false; - rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); - } - - return true; -} - -bool BitmapCache::parse_color4(const std::string& scolor, unsigned char* rgba_out) -{ - rgba_out[0] = rgba_out[1] = rgba_out[2] = 0; rgba_out[3] = 255; - if ((scolor.size() != 7 && scolor.size() != 9) || scolor.front() != '#') - return false; - const char* c = scolor.data() + 1; - for (size_t i = 0; i < scolor.size() / 2; ++i) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) - return false; - rgba_out[i] = (unsigned char)(digit1 * 16 + digit2); - } - return true; -} - //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/) { @@ -781,13 +740,9 @@ wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std if (color.empty()) bndl = new wxBitmapBundle(mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, size_t(0))); else { - //OcraftyoneTODO: replace with ColorRGB class -// ColorRGB rgb;// [3] -// decode_color(color, rgb); - unsigned char rgb[3]; - parse_color(into_u8(color), rgb); -// bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode)); - bndl = new wxBitmapBundle(mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, border_width, dark_mode)); + ColorRGB rgb;// [3] + decode_color(color, rgb); + bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode)); } m_bndl_map[bitmap_key] = bndl; } diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index e49f98b9f7..a1687b25b1 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -9,9 +9,11 @@ #include #endif +#include "libslic3r/Color.hpp" struct NSVGimage; -namespace Slic3r { namespace GUI { +namespace Slic3r { +namespace GUI { class BitmapCache { @@ -57,15 +59,12 @@ public: wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "", const float scale_in_center = 0.f); wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); - wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } + wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, true, 0); } wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false); wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false); wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); } - static bool parse_color(const std::string& scolor, unsigned char* rgb_out); - static bool parse_color4(const std::string& scolor, unsigned char* rgba_out); - private: std::map m_map; std::map m_bndl_map; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index cebd49ce75..7206b5e6c4 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -106,6 +106,78 @@ void Camera::select_view(const std::string& direction) } } +double Camera::get_near_left() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) - 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_right() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) + 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_top() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) + 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_bottom() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) - 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_width() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(0, 0); + } +} + +double Camera::get_near_height() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(1, 1); + } +} + double Camera::get_fov() const { switch (m_type) @@ -118,17 +190,14 @@ double Camera::get_fov() const }; } -void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) +void Camera::set_viewport(int x, int y, unsigned int w, unsigned int h) { - glsafe(::glViewport(0, 0, w, h)); - glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); + m_viewport = { 0, 0, int(w), int(h) }; } -void Camera::apply_view_matrix() +void Camera::apply_viewport() const { - glsafe(::glMatrixMode(GL_MODELVIEW)); - glsafe(::glLoadIdentity()); - glsafe(::glMultMatrixd(m_view_matrix.data())); + glsafe(::glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3])); } void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z) @@ -136,11 +205,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa double w = 0.0; double h = 0.0; - const double old_distance = m_distance; m_frustrum_zs = calc_tight_frustrum_zs_around(box); - if (m_distance != old_distance) - // the camera has been moved re-apply view matrix - apply_view_matrix(); if (near_z > 0.0) m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ); @@ -174,26 +239,36 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa } } - glsafe(::glMatrixMode(GL_PROJECTION)); - glsafe(::glLoadIdentity()); + apply_projection(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second); +} + +void Camera::apply_projection(double left, double right, double bottom, double top, double near_z, double far_z) +{ + assert(left != right && bottom != top && near_z != far_z); + const double inv_dx = 1.0 / (right - left); + const double inv_dy = 1.0 / (top - bottom); + const double inv_dz = 1.0 / (far_z - near_z); switch (m_type) { default: case EType::Ortho: { - glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); + m_projection_matrix.matrix() << 2.0 * inv_dx, 0.0, 0.0, -(left + right) * inv_dx, + 0.0, 2.0 * inv_dy, 0.0, -(bottom + top) * inv_dy, + 0.0, 0.0, -2.0 * inv_dz, -(near_z + far_z) * inv_dz, + 0.0, 0.0, 0.0, 1.0; break; } case EType::Perspective: { - glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); + m_projection_matrix.matrix() << 2.0 * near_z * inv_dx, 0.0, (left + right) * inv_dx, 0.0, + 0.0, 2.0 * near_z * inv_dy, (bottom + top) * inv_dy, 0.0, + 0.0, 0.0, -(near_z + far_z) * inv_dz, -2.0 * near_z * far_z * inv_dz, + 0.0, 0.0, -1.0, 0.0; break; } } - - glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data())); - glsafe(::glMatrixMode(GL_MODELVIEW)); } void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) @@ -351,8 +426,8 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo // box in eye space const BoundingBoxf3 eye_box = box.transformed(m_view_matrix); - near_z = -eye_box.max(2); - far_z = -eye_box.min(2); + near_z = -eye_box.max.z(); + far_z = -eye_box.min.z(); // apply margin near_z -= FrustrumZMargin; @@ -533,19 +608,19 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up m_distance = (position - target).norm(); const Vec3d new_position = m_target + m_distance * unit_z; - m_view_matrix(0, 0) = unit_x(0); - m_view_matrix(0, 1) = unit_x(1); - m_view_matrix(0, 2) = unit_x(2); + m_view_matrix(0, 0) = unit_x.x(); + m_view_matrix(0, 1) = unit_x.y(); + m_view_matrix(0, 2) = unit_x.z(); m_view_matrix(0, 3) = -unit_x.dot(new_position); - m_view_matrix(1, 0) = unit_y(0); - m_view_matrix(1, 1) = unit_y(1); - m_view_matrix(1, 2) = unit_y(2); + m_view_matrix(1, 0) = unit_y.x(); + m_view_matrix(1, 1) = unit_y.y(); + m_view_matrix(1, 2) = unit_y.z(); m_view_matrix(1, 3) = -unit_y.dot(new_position); - m_view_matrix(2, 0) = unit_z(0); - m_view_matrix(2, 1) = unit_z(1); - m_view_matrix(2, 2) = unit_z(2); + m_view_matrix(2, 0) = unit_z.x(); + m_view_matrix(2, 1) = unit_z.y(); + m_view_matrix(2, 2) = unit_z.z(); m_view_matrix(2, 3) = -unit_z.dot(new_position); m_view_matrix(3, 0) = 0.0; diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 401365ad4e..cfed47884c 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -107,14 +107,23 @@ public: double get_far_z() const { return m_frustrum_zs.second; } const std::pair& get_z_range() const { return m_frustrum_zs; } + double get_near_left() const; + double get_near_right() const; + double get_near_top() const; + double get_near_bottom() const; + double get_near_width() const; + double get_near_height() const; + double get_fov() const; - void apply_viewport(int x, int y, unsigned int w, unsigned int h); - void apply_view_matrix(); + void set_viewport(int x, int y, unsigned int w, unsigned int h); + void apply_viewport() const; // Calculates and applies the projection matrix tighting the frustrum z range around the given box. // If larger z span is needed, pass the desired values of near and far z (negative values are ignored) void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0); + void apply_projection(double left, double right, double bottom, double top, double near_z, double far_z); + void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor); void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 934ddb1019..b26e3e8ca6 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -511,7 +511,7 @@ void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std:: void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, const bool is_global_config) { PresetBundle *preset_bundle = wxGetApp().preset_bundle; - + auto gcflavor = preset_bundle->printers.get_edited_preset().config.option>("gcode_flavor")->value; bool have_volumetric_extrusion_rate_slope = config->option("max_volumetric_extrusion_rate_slope")->value > 0; @@ -520,27 +520,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("max_volumetric_extrusion_rate_slope_segment_length", have_volumetric_extrusion_rate_slope); if(have_volumetric_extrusion_rate_slope) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false)); if(have_volumetric_extrusion_rate_slope_segment_length==0) { - DynamicPrintConfig new_conf = *config; + DynamicPrintConfig new_conf = *config; new_conf.set_key_value("max_volumetric_extrusion_rate_slope_segment_length", new ConfigOptionInt(1)); - apply(config, &new_conf); + apply(config, &new_conf); } bool have_perimeters = config->opt_int("wall_loops") > 0; for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall", - "seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width", - "inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" }) + "seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width", + "inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" }) toggle_field(el, have_perimeters); - + bool have_infill = config->option("sparse_infill_density")->value > 0; // sparse_infill_filament uses the same logic as in Print::extruders() for (auto el : { "sparse_infill_pattern", "infill_combination", - "minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"}) + "minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"}) toggle_line(el, have_infill); // Only allow configuration of open anchors if the anchoring is enabled. bool has_infill_anchors = have_infill && config->option("infill_anchor_max")->value > 0; toggle_field("infill_anchor", has_infill_anchors); - + bool has_spiral_vase = config->opt_bool("spiral_mode"); bool has_top_solid_infill = config->opt_int("top_shell_layers") > 0; bool has_bottom_solid_infill = config->opt_int("bottom_shell_layers") > 0; @@ -548,43 +548,43 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co // solid_infill_filament uses the same logic as in Print::extruders() for (auto el : { "top_surface_pattern", "bottom_surface_pattern", "internal_solid_infill_pattern", "solid_infill_filament"}) toggle_field(el, has_solid_infill); - + for (auto el : { "infill_direction", "sparse_infill_line_width", - "sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle" }) + "sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle" }) toggle_field(el, have_infill || has_solid_infill); - + toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill); toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_solid_infill); - + // Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476). toggle_field("gap_infill_speed", have_perimeters); - + for (auto el : { "top_surface_line_width", "top_surface_speed" }) toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill)); - + bool have_default_acceleration = config->opt_float("default_acceleration") > 0; - + for (auto el : {"outer_wall_acceleration", "inner_wall_acceleration", "initial_layer_acceleration", - "top_surface_acceleration", "travel_acceleration", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration"}) + "top_surface_acceleration", "travel_acceleration", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration"}) toggle_field(el, have_default_acceleration); - + bool have_default_jerk = config->opt_float("default_jerk") > 0; - + for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk","travel_jerk", "infill_jerk"}) toggle_field(el, have_default_jerk); - + bool have_skirt = config->opt_int("skirt_loops") > 0; toggle_field("skirt_height", have_skirt && config->opt_enum("draft_shield") != dsEnabled); for (auto el : { "skirt_distance", "draft_shield"}) toggle_field(el, have_skirt); - + bool have_brim = (config->opt_enum("brim_type") != btNoBrim); toggle_field("brim_object_gap", have_brim); bool have_brim_width = (config->opt_enum("brim_type") != btNoBrim) && config->opt_enum("brim_type") != btAutoBrim; toggle_field("brim_width", have_brim_width); // wall_filament uses the same logic as in Print::extruders() toggle_field("wall_filament", have_perimeters || have_brim); - + bool have_brim_ear = (config->opt_enum("brim_type") == btEar); const auto brim_width = config->opt_float("brim_width"); // disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0 @@ -593,32 +593,32 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co // hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected toggle_line("brim_ears_max_angle", have_brim_ear); toggle_line("brim_ears_detection_length", have_brim_ear); - + // Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0); - + bool have_raft = config->opt_int("raft_layers") > 0; bool have_support_material = config->opt_bool("enable_support") || have_raft; - + SupportType support_type = config->opt_enum("support_type"); bool have_support_interface = config->opt_int("support_interface_top_layers") > 0 || config->opt_int("support_interface_bottom_layers") > 0; bool have_support_soluble = have_support_material && config->opt_float("support_top_z_distance") == 0; auto support_style = config->opt_enum("support_style"); for (auto el : { "support_style", "support_base_pattern", - "support_base_pattern_spacing", "support_expansion", "support_angle", - "support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers", - "bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance", - //BBS: add more support params to dependent of enable_support - "support_type", "support_on_build_plate_only", "support_critical_regions_only", - "support_object_xy_distance"/*, "independent_support_layer_height"*/}) + "support_base_pattern_spacing", "support_expansion", "support_angle", + "support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers", + "bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance", + //BBS: add more support params to dependent of enable_support + "support_type", "support_on_build_plate_only", "support_critical_regions_only", + "support_object_xy_distance"/*, "independent_support_layer_height"*/}) toggle_field(el, have_support_material); toggle_field("support_threshold_angle", have_support_material && is_auto(support_type)); //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); - + bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type); bool support_is_normal_tree = support_is_tree && support_style != smsOrganic && - // Orca: use organic as default - support_style != smsDefault; + // Orca: use organic as default + support_style != smsDefault; bool support_is_organic = support_is_tree && !support_is_normal_tree; // settings shared by normal and organic trees for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" }) @@ -629,85 +629,85 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co // settings specific to organic trees for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall"}) toggle_line(el, support_is_organic); - + toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim")); // non-organic tree support use max_bridge_length instead of bridge_no_support toggle_line("max_bridge_length", support_is_normal_tree); toggle_line("bridge_no_support", !support_is_normal_tree); - + // This is only supported for auto normal tree toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_normal_tree); - + for (auto el : { "support_interface_spacing", "support_interface_filament", - "support_interface_loop_pattern", "support_bottom_interface_spacing" }) + "support_interface_loop_pattern", "support_bottom_interface_spacing" }) toggle_field(el, have_support_material && have_support_interface); - + bool have_skirt_height = have_skirt && - (config->opt_int("skirt_height") > 1 || config->opt_enum("draft_shield") != dsEnabled); + (config->opt_int("skirt_height") > 1 || config->opt_enum("draft_shield") != dsEnabled); toggle_line("support_speed", have_support_material || have_skirt_height); toggle_line("support_interface_speed", have_support_material && have_support_interface); - + // BBS //toggle_field("support_material_synchronize_layers", have_support_soluble); - + toggle_field("inner_wall_line_width", have_perimeters || have_skirt || have_brim); toggle_field("support_filament", have_support_material || have_skirt); - + toggle_line("raft_contact_distance", have_raft && !have_support_soluble); - + // Orca: Raft, grid, snug and organic supports use these two parameters to control the size & density of the "brim"/flange for (auto el : { "raft_first_layer_expansion", "raft_first_layer_density"}) toggle_field(el, have_support_material && !(support_is_normal_tree && !have_raft)); - + bool has_ironing = (config->opt_enum("ironing_type") != IroningType::NoIroning); for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_speed", "ironing_angle" }) toggle_line(el, has_ironing); - + // bool have_sequential_printing = (config->opt_enum("print_sequence") == PrintSequence::ByObject); // for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) // toggle_field(el, have_sequential_printing); - + bool have_ooze_prevention = config->opt_bool("ooze_prevention"); toggle_field("standby_temperature_delta", have_ooze_prevention); - + bool have_prime_tower = config->opt_bool("enable_prime_tower"); for (auto el : { "prime_tower_width", "prime_tower_brim_width"}) toggle_line(el, have_prime_tower); - + bool purge_in_primetower = preset_bundle->printers.get_edited_preset().config.opt_bool("purge_in_prime_tower"); - + for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_bridging", "wipe_tower_no_sparse_layers"}) toggle_line(el, have_prime_tower && purge_in_primetower); - + toggle_line("prime_volume",have_prime_tower && !purge_in_primetower); - + for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"}) toggle_field(el, have_prime_tower); - - // BBS: MusangKing - Hide "Independent support layer height" option + + // BBS: MusangKing - Hide "Independent support layer height" option toggle_line("independent_support_layer_height", have_support_material && !have_prime_tower); - + bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall"); toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters); - + bool has_overhang_speed = config->opt_bool("enable_overhang_speed"); for (auto el : {"overhang_speed_classic", "overhang_1_4_speed", - "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"}) + "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"}) toggle_line(el, has_overhang_speed); bool has_overhang_speed_classic = config->opt_bool("overhang_speed_classic"); toggle_line("slowdown_for_curled_perimeters",!has_overhang_speed_classic && has_overhang_speed); - + toggle_line("flush_into_objects", !is_global_config); - + bool has_fuzzy_skin = (config->opt_enum("fuzzy_skin") != FuzzySkinType::None); for (auto el : { "fuzzy_skin_thickness", "fuzzy_skin_point_distance"}) toggle_line(el, has_fuzzy_skin); - + bool have_arachne = config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne; for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "min_feature_size", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"}) + "min_feature_size", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"}) toggle_line(el, have_arachne); toggle_field("detect_thin_wall", !have_arachne); @@ -719,23 +719,30 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line(el, gcflavor == gcfKlipper); if(gcflavor == gcfKlipper) toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable")); - + bool have_make_overhang_printable = config->opt_bool("make_overhang_printable"); toggle_line("make_overhang_printable_angle", have_make_overhang_printable); toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable); - + toggle_line("exclude_object", gcflavor == gcfKlipper); - + toggle_line("min_width_top_surface",config->opt_bool("only_one_wall_top")); - + for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" }) toggle_line(el, config->opt_bool("hole_to_polyhole")); - + bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall"); bool has_overhang_reverse = config->opt_bool("overhang_reverse"); bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase; toggle_field("overhang_reverse", allow_overhang_reverse); toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse); + toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse); + bool has_overhang_reverse_internal_only = config->opt_bool("overhang_reverse_internal_only"); + if (has_overhang_reverse_internal_only){ + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("overhang_reverse_threshold", new ConfigOptionFloatOrPercent(0,true)); + apply(config, &new_conf); + } toggle_line("timelapse_type", is_BBL_Printer); } diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b5e4b7f9b1..6dff200df5 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -36,6 +36,7 @@ #include "libslic3r/Config.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/Model.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_Utils.hpp" @@ -777,9 +778,9 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector* are not compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials")); wxString text; if (all_printers) { diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 6aa029dde1..3e63cf192d 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1619,8 +1619,7 @@ boost::any& ColourPicker::get_value() if (colour == wxTransparentColour) m_value = std::string(""); else { - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); - m_value = clr_str.ToStdString(); + m_value = encode_color(ColorRGB(colour.Red(), colour.Green(), colour.Blue())); } return m_value; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3d0e4f2b9c..5382ce746b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -90,42 +90,6 @@ static EMoveType buffer_type(unsigned char id) { return static_cast(static_cast(EMoveType::Retract) + id); } -static std::array decode_color(const std::string& color) { - static const float INV_255 = 1.0f / 255.0f; - - std::array ret = { 0.0f, 0.0f, 0.0f, 1.0f }; - const char* c = color.data() + 1; - if (color.size() == 7 && color.front() == '#') { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) - break; - - ret[j] = float(digit1 * 16 + digit2) * INV_255; - } - } - else if (color.size() == 9 && color.front() == '#') { - for (size_t j = 0; j < 4; ++j) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) - break; - - ret[j] = float(digit1 * 16 + digit2) * INV_255; - } - } - return ret; -} - -static std::vector> decode_colors(const std::vector& colors) { - std::vector> output(colors.size(), { 0.0f, 0.0f, 0.0f, 1.0f }); - for (size_t i = 0; i < colors.size(); ++i) { - output[i] = decode_color(colors[i]); - } - return output; -} - // Round to a bin with minimum two digits resolution. // Equivalent to conversion to string with sprintf(buf, "%.2g", value) and conversion back to float, but faster. static float round_to_bin(const float value) @@ -263,7 +227,7 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move move.volumetric_rate(), move.layer_duration, move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } }); } -GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const +ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value) const { // Input value scaled to the colors range const float step = step_size(); @@ -280,15 +244,8 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) con const size_t color_low_idx = std::clamp(static_cast(global_t), 0, color_max_idx); const size_t color_high_idx = std::clamp(color_low_idx + 1, 0, color_max_idx); - // Compute how far the value is between the low and high colors so that they can be interpolated - const float local_t = std::clamp(global_t - static_cast(color_low_idx), 0.0f, 1.0f); - // Interpolate between the low and high colors to find exactly which color the input value should get - Color ret = { 0.0f, 0.0f, 0.0f, 1.0f }; - for (unsigned int i = 0; i < 3; ++i) { - ret[i] = lerp(Range_Colors[color_low_idx][i], Range_Colors[color_high_idx][i], local_t); - } - return ret; + return lerp(Range_Colors[color_low_idx], Range_Colors[color_high_idx], global_t - static_cast(color_low_idx)); } float GCodeViewer::Extrusions::Range::step_size() const { @@ -331,7 +288,7 @@ void GCodeViewer::SequentialView::Marker::init(std::string filename) } else { m_model.init_from_file(filename); } - m_model.set_color(-1, {1.0f, 1.0f, 1.0f, 0.5f}); + m_model.set_color({ 1.0f, 1.0f, 1.0f, 0.5f }); } void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) @@ -345,7 +302,7 @@ void GCodeViewer::SequentialView::Marker::update_curr_move(const GCodeProcessorR } //BBS: GUI refactor: add canvas size from parameters -void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_height, const EViewType& view_type) const +void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_height, const EViewType& view_type) { if (!m_visible) return; @@ -360,13 +317,16 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he shader->start_using(); shader->set_uniform("emission_factor", 0.0f); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixf(m_world_transform.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d model_matrix = m_world_transform.cast(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_model.render(); - glsafe(::glPopMatrix()); - shader->stop_using(); glsafe(::glDisable(GL_BLEND)); @@ -584,11 +544,11 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, f return ret; }; - static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; - static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; - static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; - static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; if (!m_visible || !wxGetApp().show_gcode_window() || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) return; @@ -713,7 +673,7 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished mapping file " << m_filename; } } -void GCodeViewer::SequentialView::render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type) const +void GCodeViewer::SequentialView::render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type) { if (has_render_path) marker.render(canvas_width, canvas_height, view_type); @@ -728,7 +688,7 @@ if (has_render_path) gcode_window.render(legend_height + 2, std::max(10.f, (float)canvas_height - 40), (float)canvas_width - (float)right_margin, static_cast(gcode_ids[current.last])); } -const std::vector GCodeViewer::Extrusion_Role_Colors {{ +const std::vector GCodeViewer::Extrusion_Role_Colors{ { { 0.90f, 0.70f, 0.70f, 1.0f }, // erNone { 1.00f, 0.90f, 0.30f, 1.0f }, // erPerimeter { 1.00f, 0.49f, 0.22f, 1.0f }, // erExternalPerimeter @@ -750,7 +710,7 @@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.37f, 0.82f, 0.58f, 1.0f } // erCustom }}; -const std::vector GCodeViewer::Options_Colors {{ +const std::vector GCodeViewer::Options_Colors{ { { 0.803f, 0.135f, 0.839f, 1.0f }, // Retractions { 0.287f, 0.679f, 0.810f, 1.0f }, // Unretractions { 0.900f, 0.900f, 0.900f, 1.0f }, // Seams @@ -760,7 +720,7 @@ const std::vector GCodeViewer::Options_Colors {{ { 0.886f, 0.825f, 0.262f, 1.0f } // CustomGCodes }}; -const std::vector GCodeViewer::Travel_Colors {{ +const std::vector GCodeViewer::Travel_Colors{ { { 0.219f, 0.282f, 0.609f, 1.0f }, // Move { 0.112f, 0.422f, 0.103f, 1.0f }, // Extrude { 0.505f, 0.064f, 0.028f, 1.0f } // Retract @@ -768,7 +728,7 @@ const std::vector GCodeViewer::Travel_Colors {{ // Normal ranges // blue to red -const std::vector GCodeViewer::Range_Colors{{ +const std::vector GCodeViewer::Range_Colors{ { decode_color_to_float_array("#0b2c7a"), // bluish decode_color_to_float_array("#135985"), decode_color_to_float_array("#1c8891"), @@ -782,8 +742,8 @@ const std::vector GCodeViewer::Range_Colors{{ decode_color_to_float_array("#942616") // reddish }}; -const GCodeViewer::Color GCodeViewer::Wipe_Color = { 1.0f, 1.0f, 0.0f, 1.0f }; -const GCodeViewer::Color GCodeViewer::Neutral_Color = { 0.25f, 0.25f, 0.25f, 1.0f }; +const ColorRGBA GCodeViewer::Wipe_Color = ColorRGBA::YELLOW(); +const ColorRGBA GCodeViewer::Neutral_Color = ColorRGBA::DARK_GRAY(); GCodeViewer::GCodeViewer() { @@ -858,8 +818,8 @@ void GCodeViewer::init(ConfigOptionMode mode, PresetBundle* preset_bundle) } case EMoveType::Travel: { buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line; - buffer.vertices.format = VBuffer::EFormat::PositionNormal3; - buffer.shader = "toolpaths_lines"; + buffer.vertices.format = VBuffer::EFormat::Position; + buffer.shader = "flat"; break; } } @@ -963,7 +923,7 @@ std::vector GCodeViewer::get_plater_extruder() //BBS: always load shell at preview void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume, - const std::vector& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode) + const std::vector& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode) { // avoid processing if called with the same gcode_result if (m_last_result_id == gcode_result.id) { @@ -1018,7 +978,7 @@ m_sequential_view.m_show_gcode_window = false; //BBS: always load shell at preview /*if (wxGetApp().is_editor()) { - //load_shells(print, initialized); + load_shells(print); } else {*/ //BBS: add only gcode mode @@ -1140,13 +1100,13 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v if (m_view_type == EViewType::Tool && !gcode_result.extruder_colors.empty()) { // update tool colors from config stored in the gcode - m_tools.m_tool_colors = decode_colors(gcode_result.extruder_colors); + decode_colors(gcode_result.extruder_colors, m_tools.m_tool_colors); m_tools.m_tool_visibles = std::vector(m_tools.m_tool_colors.size()); for (auto item: m_tools.m_tool_visibles) item = true; } else { // update tool colors - m_tools.m_tool_colors = decode_colors(str_tool_colors); + decode_colors(str_tool_colors, m_tools.m_tool_colors); m_tools.m_tool_visibles = std::vector(m_tools.m_tool_colors.size()); for (auto item : m_tools.m_tool_visibles) item = true; } @@ -1154,9 +1114,11 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v for (int i = 0; i < m_tools.m_tool_colors.size(); i++) { m_tools.m_tool_colors[i] = adjust_color_for_rendering(m_tools.m_tool_colors[i]); } - // ensure there are enough colors defined + ColorRGBA default_color; + decode_color("#FF8000", default_color); + // ensure there are enough colors defined while (m_tools.m_tool_colors.size() < std::max(size_t(1), gcode_result.extruders_count)) { - m_tools.m_tool_colors.push_back(decode_color("#FF8000")); + m_tools.m_tool_colors.push_back(default_color); m_tools.m_tool_visibles.push_back(true); } @@ -1247,7 +1209,7 @@ void GCodeViewer::reset() m_paths_bounding_box = BoundingBoxf3(); m_max_bounding_box = BoundingBoxf3(); m_max_print_height = 0.0f; - m_tools.m_tool_colors = std::vector(); + m_tools.m_tool_colors = std::vector(); m_tools.m_tool_visibles = std::vector(); m_extruders_count = 0; m_extruder_ids = std::vector(); @@ -1276,14 +1238,11 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin) m_statistics.total_instances_gpu_size = 0; #endif // ENABLE_GCODE_VIEWER_STATISTICS - render_shells(); - // Orca: add shell overlay effect - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - if (m_roles.empty()) return; - glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_DEPTH_TEST)); + render_shells(); render_toolpaths(); float legend_height = 0.0f; render_legend(legend_height, canvas_width, canvas_height, right_margin); @@ -1346,12 +1305,12 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai #if 1 Camera camera; - camera.apply_viewport(0,0,thumbnail_data.width, thumbnail_data.height); + camera.set_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.apply_viewport(); camera.set_scene_box(plate_box); camera.set_type(Camera::EType::Ortho); camera.set_target(center); camera.select_view("top"); - camera.apply_view_matrix(); camera.zoom_to_box(plate_box, 1.0f); camera.apply_projection(plate_box); @@ -1365,7 +1324,7 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415. assert(!path.sizes.empty()); assert(!path.offsets.empty()); - glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + shader.set_uniform(uniform_color, path.color); glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_triangles_calls_count; @@ -1387,7 +1346,7 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai } if (range.vbo > 0) { - buffer.model.model.set_color(-1, range.color); + buffer.model.model.set_color(range.color); buffer.model.model.render_instanced(range.vbo, range.count); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_instanced_models_calls_count; @@ -1400,7 +1359,7 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai #if ENABLE_GCODE_VIEWER_STATISTICS auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader) { #else - auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader) { + auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader, int position_id, int normal_id) { #endif // ENABLE_GCODE_VIEWER_STATISTICS struct Range @@ -1416,12 +1375,16 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai const IBuffer& i_buffer = buffer.indices[j]; buffer_range.last = buffer_range.first + i_buffer.count / indices_per_instance; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableVertexAttribArray(position_id)); + } bool has_normals = buffer.vertices.normal_size_floats() > 0; if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableVertexAttribArray(normal_id)); + } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); @@ -1444,11 +1407,11 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); buffer_range.first = buffer_range.last; @@ -1464,10 +1427,15 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai if (!buffer.visible || !buffer.has_data()) continue; - GLShaderProgram* shader = opengl_manager.get_shader("cali"); + GLShaderProgram* shader = opengl_manager.get_shader("flat"); if (shader != nullptr) { shader->start_using(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + int position_id = shader->get_attrib_location("v_position"); + int normal_id = shader->get_attrib_location("v_normal"); + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { //shader->set_uniform("emission_factor", 0.25f); render_as_instanced_model(buffer, *shader); @@ -1475,7 +1443,7 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai } else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { //shader->set_uniform("emission_factor", 0.25f); - render_as_batched_model(buffer, *shader); + render_as_batched_model(buffer, *shader, position_id, normal_id); //shader->set_uniform("emission_factor", 0.0f); } else { @@ -1493,12 +1461,16 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai continue; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableVertexAttribArray(position_id)); + } bool has_normals = false;// buffer.vertices.normal_size_floats() > 0; if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableVertexAttribArray(normal_id)); + } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); @@ -1514,11 +1486,12 @@ void GCodeViewer::_render_calibration_thumbnail_internal(ThumbnailData& thumbnai } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } } @@ -1904,7 +1877,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const return; // collect color information to generate materials - std::vector colors; + std::vector colors; for (const RenderPath& path : t_buffer.render_paths) { colors.push_back(path.color); } @@ -1926,10 +1899,10 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SoftFever_VERSION); unsigned int colors_count = 1; - for (const Color& color : colors) { + for (const ColorRGBA& color : colors) { fprintf(fp, "\nnewmtl material_%d\n", colors_count++); fprintf(fp, "Ka 1 1 1\n"); - fprintf(fp, "Kd %g %g %g\n", color[0], color[1], color[2]); + fprintf(fp, "Kd %g %g %g\n", color.r(), color.g(), color.b()); fprintf(fp, "Ks 0 0 0\n"); } @@ -1990,7 +1963,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const } size_t i = 0; - for (const Color& color : colors) { + for (const ColorRGBA& color : colors) { // save material triangles to file fprintf(fp, "\nusemtl material_%zu\n", i + 1); fprintf(fp, "# triangles material %zu\n", i + 1); @@ -2059,29 +2032,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const log_memory_used(label, vertices_size + indices_size); }; - // format data into the buffers to be rendered as points - auto add_vertices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) { - vertices.push_back(curr.position.x()); - vertices.push_back(curr.position.y()); - vertices.push_back(curr.position.z()); - }; - auto add_indices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, - unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { - buffer.add_path(curr, ibuffer_id, indices.size(), move_id); - indices.push_back(static_cast(indices.size())); - }; - // format data into the buffers to be rendered as lines auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) { - auto add_vertex = [&vertices](const Vec3f& position, const Vec3f& normal) { + auto add_vertex = [&vertices](const Vec3f& position) { // add position vertices.push_back(position.x()); vertices.push_back(position.y()); vertices.push_back(position.z()); - // add normal - vertices.push_back(normal.x()); - vertices.push_back(normal.y()); - vertices.push_back(normal.z()); }; // x component of the normal to the current segment (the normal is parallel to the XY plane) //BBS: Has modified a lot for this function to support arc move @@ -2089,13 +2046,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const for (size_t i = 0; i < loop_num + 1; i++) { const Vec3f &previous = (i == 0? prev.position : curr.interpolation_points[i-1]); const Vec3f ¤t = (i == loop_num? curr.position : curr.interpolation_points[i]); - const Vec3f dir = (current - previous).normalized(); - Vec3f normal(dir.y(), -dir.x(), 0.0); - normal.normalize(); // add previous vertex - add_vertex(previous, normal); + add_vertex(previous); // add current vertex - add_vertex(current, normal); + add_vertex(current); } }; //BBS: modify a lot to support arc travel @@ -2332,28 +2286,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const }; // format data into the buffers to be rendered as batched model - auto add_vertices_as_model_batch = [](const GCodeProcessorResult::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) { + auto add_vertices_as_model_batch = [](const GCodeProcessorResult::MoveVertex& curr, const GLModel::Geometry& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) { const double width = static_cast(1.5f * curr.width); const double height = static_cast(1.5f * curr.height); const Transform3d trafo = Geometry::assemble_transform((curr.position - 0.5f * curr.height * Vec3f::UnitZ()).cast(), Vec3d::Zero(), { width, width, height }); const Eigen::Matrix normal_matrix = trafo.matrix().template block<3, 3>(0, 0).inverse().transpose(); - for (const auto& entity : data.entities) { - // append vertices - for (size_t i = 0; i < entity.positions.size(); ++i) { - // append position - const Vec3d position = trafo * entity.positions[i].cast(); - vertices.push_back(static_cast(position.x())); - vertices.push_back(static_cast(position.y())); - vertices.push_back(static_cast(position.z())); + // append vertices + const size_t vertices_count = data.vertices_count(); + for (size_t i = 0; i < vertices_count; ++i) { + // append position + const Vec3d position = trafo * data.extract_position_3(i).cast(); + vertices.push_back(float(position.x())); + vertices.push_back(float(position.y())); + vertices.push_back(float(position.z())); - // append normal - const Vec3d normal = normal_matrix * entity.normals[i].cast(); - vertices.push_back(static_cast(normal.x())); - vertices.push_back(static_cast(normal.y())); - vertices.push_back(static_cast(normal.z())); - } + // append normal + const Vec3d normal = normal_matrix * data.extract_normal_3(i).cast(); + vertices.push_back(float(normal.x())); + vertices.push_back(float(normal.y())); + vertices.push_back(float(normal.z())); } // append instance position @@ -2364,11 +2317,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const instances_ids.push_back(move_id); }; - auto add_indices_as_model_batch = [](const GLModel::InitializationData& data, IndexBuffer& indices, IBufferType base_index) { - for (const auto& entity : data.entities) { - for (size_t i = 0; i < entity.indices.size(); ++i) { - indices.push_back(static_cast(entity.indices[i] + base_index)); - } + auto add_indices_as_model_batch = [](const GLModel::Geometry& data, IndexBuffer& indices, IBufferType base_index) { + const size_t indices_count = data.indices_count(); + for (size_t i = 0; i < indices_count; ++i) { + indices.push_back(static_cast(data.extract_index(i) + base_index)); } }; @@ -2543,7 +2495,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const switch (t_buffer.render_primitive_type) { - case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast(v_multibuffer.size()) - 1, v_buffer, move_id); break; } case TBuffer::ERenderPrimitiveType::InstancedModel: @@ -2931,8 +2882,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - indiced_size_to_add) { i_multibuffer.push_back(IndexBuffer()); vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); - if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && - t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { Path& last_path = t_buffer.paths.back(); last_path.add_sub_path(prev, static_cast(i_multibuffer.size()) - 1, 0, move_id - 1); } @@ -2949,8 +2899,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const curr_vertex_buffer.second = 0; vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); - if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && - t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { Path& last_path = t_buffer.paths.back(); last_path.add_sub_path(prev, static_cast(i_multibuffer.size()) - 1, 0, move_id - 1); } @@ -2960,11 +2909,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const switch (t_buffer.render_primitive_type) { - case TBuffer::ERenderPrimitiveType::Point: { - add_indices_as_point(curr, t_buffer, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id); - curr_vertex_buffer.second += t_buffer.max_vertices_per_segment(); - break; - } case TBuffer::ERenderPrimitiveType::Line: { add_indices_as_line(prev, curr, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, move_id); break; @@ -3131,9 +3075,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const } //BBS: always load shell when preview -void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing) +void GCodeViewer::load_shells(const Print& print, bool force_previewing) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": force_previewing=%1%") %force_previewing; if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) { //BBS: update force previewing logic if (force_previewing) @@ -3181,7 +3125,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p instance_ids.resize(instance_index); size_t current_volumes_count = m_shells.volumes.volumes.size(); - m_shells.volumes.load_object(model_obj, object_idx, instance_ids, "object", initialized); + m_shells.volumes.load_object(model_obj, object_idx, instance_ids); // adjust shells' z if raft is present const SlicingParameters& slicing_parameters = obj->slicing_parameters(); @@ -3231,7 +3175,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p for (GLVolume* volume : m_shells.volumes.volumes) { volume->zoom_to_volumes = false; - volume->color[3] = 0.5f; + volume->color.a(0.5f); volume->force_native_color = true; volume->set_render_color(); //BBS: add shell bounding box logic @@ -3254,7 +3198,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": enter, m_buffers size %1%!")%m_buffers.size(); auto extrusion_color = [this](const Path& path) { - Color color; + ColorRGBA color; switch (m_view_type) { case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } @@ -3269,7 +3213,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Tool: { color = m_tools.m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { if (path.cp_color_id >= static_cast(m_tools.m_tool_colors.size())) - color = { 0.5f, 0.5f, 0.5f, 1.0f }; + color = ColorRGBA::GRAY(); else { color = m_tools.m_tool_colors[path.cp_color_id]; color = adjust_color_for_rendering(color); @@ -3282,7 +3226,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool color = {id, role, id, 1.0f}; break; } - default: { color = { 1.0f, 1.0f, 1.0f, 1.0f }; break; } + default: { color = ColorRGBA::WHITE(); break; } } return color; @@ -3515,7 +3459,7 @@ m_no_render_path = false; if (m_sequential_view.current.last < sub_path.first.s_id || sub_path.last.s_id < m_sequential_view.current.first) continue; - Color color; + ColorRGBA color; switch (path.type) { case EMoveType::Tool_change: @@ -3560,10 +3504,6 @@ m_no_render_path = false; unsigned int size_in_indices = 0; switch (buffer.render_primitive_type) { - case TBuffer::ERenderPrimitiveType::Point: { - size_in_indices = buffer.indices_per_segment(); - break; - } case TBuffer::ERenderPrimitiveType::Line: case TBuffer::ERenderPrimitiveType::Triangle: { // BBS: modify to support moves which has internal point @@ -3807,69 +3747,20 @@ m_no_render_path = false; void GCodeViewer::render_toolpaths() { -#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - float point_size = 20.0f; -#else - float point_size = 0.8f; -#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - std::array light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f }; const Camera& camera = wxGetApp().plater()->get_camera(); - double zoom = camera.get_zoom(); - const std::array& viewport = camera.get_viewport(); - float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast(viewport[3]) / (2.0f * static_cast(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : - static_cast(viewport[3]) * 0.0005; + const double zoom = camera.get_zoom(); - auto shader_init_as_points = [zoom, point_size, near_plane_height](GLShaderProgram& shader) { -#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - shader.set_uniform("use_fixed_screen_size", 1); -#else - shader.set_uniform("use_fixed_screen_size", 0); -#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS - shader.set_uniform("zoom", zoom); - shader.set_uniform("percent_outline_radius", 0.0f); - shader.set_uniform("percent_center_radius", 0.33f); - shader.set_uniform("point_size", point_size); - shader.set_uniform("near_plane_height", near_plane_height); - }; - - auto render_as_points = [ -#if ENABLE_GCODE_VIEWER_STATISTICS - this -#endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::reverse_iterator it_path, std::vector::reverse_iterator it_end, GLShaderProgram& shader, int uniform_color) { - glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); - glsafe(::glEnable(GL_POINT_SPRITE)); - - for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { - const RenderPath& path = *it; - // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415. - assert(! path.sizes.empty()); - assert(! path.offsets.empty()); - glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); - glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); -#if ENABLE_GCODE_VIEWER_STATISTICS - ++m_statistics.gl_multi_points_calls_count; -#endif // ENABLE_GCODE_VIEWER_STATISTICS - } - - glsafe(::glDisable(GL_POINT_SPRITE)); - glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); - }; - - auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) { - shader.set_uniform("light_intensity", light_intensity); - }; auto render_as_lines = [ #if ENABLE_GCODE_VIEWER_STATISTICS this #endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::reverse_iterator it_path, std::vector::reverse_iterator it_end, GLShaderProgram& shader, int uniform_color) { + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { const RenderPath& path = *it; // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415. assert(! path.sizes.empty()); assert(! path.offsets.empty()); - glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + shader.set_uniform(uniform_color, path.color); glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_lines_calls_count; @@ -3881,13 +3772,13 @@ void GCodeViewer::render_toolpaths() #if ENABLE_GCODE_VIEWER_STATISTICS this #endif // ENABLE_GCODE_VIEWER_STATISTICS - ](std::vector::reverse_iterator it_path, std::vector::reverse_iterator it_end, GLShaderProgram& shader, int uniform_color) { + ](std::vector::iterator it_path, std::vector::iterator it_end, GLShaderProgram& shader, int uniform_color) { for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) { const RenderPath& path = *it; // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415. assert(! path.sizes.empty()); assert(! path.offsets.empty()); - glsafe(::glUniform4fv(uniform_color, 1, static_cast(path.color.data()))); + shader.set_uniform(uniform_color, path.color); glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_multi_triangles_calls_count; @@ -3909,7 +3800,7 @@ void GCodeViewer::render_toolpaths() } if (range.vbo > 0) { - buffer.model.model.set_color(-1, range.color); + buffer.model.model.set_color(range.color); buffer.model.model.render_instanced(range.vbo, range.count); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_instanced_models_calls_count; @@ -3920,9 +3811,9 @@ void GCodeViewer::render_toolpaths() }; #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader) { + auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader, int position_id, int normal_id) { #else - auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader) { + auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader, int position_id, int normal_id) { #endif // ENABLE_GCODE_VIEWER_STATISTICS struct Range @@ -3932,30 +3823,34 @@ void GCodeViewer::render_toolpaths() bool intersects(const Range& other) const { return (other.last < first || other.first > last) ? false : true; } }; Range buffer_range = { 0, 0 }; - size_t indices_per_instance = buffer.model.data.indices_count(); + const size_t indices_per_instance = buffer.model.data.indices_count(); for (size_t j = 0; j < buffer.indices.size(); ++j) { const IBuffer& i_buffer = buffer.indices[j]; buffer_range.last = buffer_range.first + i_buffer.count / indices_per_instance; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - bool has_normals = buffer.vertices.normal_size_floats() > 0; + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableVertexAttribArray(position_id)); + } + const bool has_normals = buffer.vertices.normal_size_floats() > 0; if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableVertexAttribArray(normal_id)); + } } glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); for (auto& range : buffer.model.instances.render_ranges.ranges) { - Range range_range = { range.offset, range.offset + range.count }; + const Range range_range = { range.offset, range.offset + range.count }; if (range_range.intersects(buffer_range)) { shader.set_uniform("uniform_color", range.color); - unsigned int offset = (range_range.first > buffer_range.first) ? range_range.first - buffer_range.first : 0; - size_t offset_bytes = static_cast(offset) * indices_per_instance * sizeof(IBufferType); - Range render_range = { std::max(range_range.first, buffer_range.first), std::min(range_range.last, buffer_range.last) }; - size_t count = static_cast(render_range.last - render_range.first) * indices_per_instance; + const unsigned int offset = (range_range.first > buffer_range.first) ? range_range.first - buffer_range.first : 0; + const size_t offset_bytes = static_cast(offset) * indices_per_instance * sizeof(IBufferType); + const Range render_range = { std::max(range_range.first, buffer_range.first), std::min(range_range.last, buffer_range.last) }; + const size_t count = static_cast(render_range.last - render_range.first) * indices_per_instance; if (count > 0) { glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)count, GL_UNSIGNED_SHORT, (const void*)offset_bytes)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -3967,10 +3862,10 @@ void GCodeViewer::render_toolpaths() glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); buffer_range.first = buffer_range.last; @@ -3981,9 +3876,8 @@ void GCodeViewer::render_toolpaths() return (zoom < 5.0) ? 1.0 : (1.0 + 5.0 * (zoom - 5.0) / (100.0 - 5.0)); }; - unsigned char begin_id = buffer_id(EMoveType::Retract); - unsigned char end_id = buffer_id(EMoveType::Count); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":begin_id %1%, end_id %2% ")%(int)begin_id %(int)end_id; + const unsigned char begin_id = buffer_id(EMoveType::Retract); + const unsigned char end_id = buffer_id(EMoveType::Count); for (unsigned char i = begin_id; i < end_id; ++i) { TBuffer& buffer = m_buffers[i]; @@ -3991,119 +3885,135 @@ void GCodeViewer::render_toolpaths() continue; GLShaderProgram* shader = wxGetApp().get_shader(buffer.shader.c_str()); - if (shader != nullptr) { - shader->start_using(); + if (shader == nullptr) + continue; - if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { - shader->set_uniform("emission_factor", 0.25f); - render_as_instanced_model(buffer, *shader); - shader->set_uniform("emission_factor", 0.0f); - } - else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { - shader->set_uniform("emission_factor", 0.25f); - render_as_batched_model(buffer, *shader); - shader->set_uniform("emission_factor", 0.0f); - } - else { - switch (buffer.render_primitive_type) { - case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break; - case TBuffer::ERenderPrimitiveType::Line: shader_init_as_lines(*shader); break; - default: break; - } - int uniform_color = shader->get_uniform_location("uniform_color"); - auto it_path = buffer.render_paths.rbegin(); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":buffer indices size %1%, render_path size %2% ")%buffer.indices.size() %buffer.render_paths.size(); - unsigned int indices_count = static_cast(buffer.indices.size()); - for (unsigned int index = 0; index < indices_count; ++index) { - unsigned int ibuffer_id = indices_count - index - 1; - const IBuffer& i_buffer = buffer.indices[ibuffer_id]; - // Skip all paths with ibuffer_id < ibuffer_id. - for (; it_path != buffer.render_paths.rend() && it_path->ibuffer_id > ibuffer_id; ++ it_path) ; - if (it_path == buffer.render_paths.rend() || it_path->ibuffer_id < ibuffer_id) - // Not found. This shall not happen. - continue; + shader->start_using(); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); - glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - bool has_normals = buffer.vertices.normal_size_floats() > 0; - if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - } + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_normal_matrix", (Matrix3d)Matrix3d::Identity()); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); - - // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. - switch (buffer.render_primitive_type) - { - case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(it_path, buffer.render_paths.rend(), *shader, uniform_color); - break; - } - case TBuffer::ERenderPrimitiveType::Line: { - glsafe(::glLineWidth(static_cast(line_width(zoom)))); - render_as_lines(it_path, buffer.render_paths.rend(), *shader, uniform_color); - break; - } - case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(it_path, buffer.render_paths.rend(), *shader, uniform_color); - break; - } - default: { break; } - } - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - } - - shader->stop_using(); + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { + shader->set_uniform("emission_factor", 0.25f); + render_as_instanced_model(buffer, *shader); + shader->set_uniform("emission_factor", 0.0f); } + else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { + shader->set_uniform("emission_factor", 0.25f); + const int position_id = shader->get_attrib_location("v_position"); + const int normal_id = shader->get_attrib_location("v_normal"); + render_as_batched_model(buffer, *shader, position_id, normal_id); + shader->set_uniform("emission_factor", 0.0f); + } + else { + const int position_id = shader->get_attrib_location("v_position"); + const int normal_id = shader->get_attrib_location("v_normal"); + const int uniform_color = shader->get_uniform_location("uniform_color"); + + auto it_path = buffer.render_paths.begin(); + for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast(buffer.indices.size()); ++ibuffer_id) { + const IBuffer& i_buffer = buffer.indices[ibuffer_id]; + // Skip all paths with ibuffer_id < ibuffer_id. + for (; it_path != buffer.render_paths.end() && it_path->ibuffer_id < ibuffer_id; ++it_path); + if (it_path == buffer.render_paths.end() || it_path->ibuffer_id > ibuffer_id) + // Not found. This shall not happen. + continue; + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, buffer.vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableVertexAttribArray(position_id)); + } + const bool has_normals = buffer.vertices.normal_size_floats() > 0; + if (has_normals) { + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, buffer.vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableVertexAttribArray(normal_id)); + } + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + + // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. + switch (buffer.render_primitive_type) + { + case TBuffer::ERenderPrimitiveType::Line: { + glsafe(::glLineWidth(static_cast(line_width(zoom)))); + render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color); + break; + } + case TBuffer::ERenderPrimitiveType::Triangle: { + render_as_triangles(it_path, buffer.render_paths.end(), *shader, uniform_color); + break; + } + default: { break; } + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } + } + + shader->stop_using(); } #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_sequential_range_cap = [this] + auto render_sequential_range_cap = [this, &camera] #else - auto render_sequential_range_cap = [] + auto render_sequential_range_cap = [&camera] #endif // ENABLE_GCODE_VIEWER_STATISTICS (const SequentialRangeCap& cap) { - GLShaderProgram* shader = wxGetApp().get_shader(cap.buffer->shader.c_str()); - if (shader != nullptr) { - shader->start_using(); + const TBuffer* buffer = cap.buffer; + GLShaderProgram* shader = wxGetApp().get_shader(buffer->shader.c_str()); + if (shader == nullptr) + return; - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, cap.vbo)); - glsafe(::glVertexPointer(cap.buffer->vertices.position_size_floats(), GL_FLOAT, cap.buffer->vertices.vertex_size_bytes(), (const void*)cap.buffer->vertices.position_offset_bytes())); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - bool has_normals = cap.buffer->vertices.normal_size_floats() > 0; - if (has_normals) { - glsafe(::glNormalPointer(GL_FLOAT, cap.buffer->vertices.vertex_size_bytes(), (const void*)cap.buffer->vertices.normal_offset_bytes())); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + shader->start_using(); + + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_normal_matrix", (Matrix3d)Matrix3d::Identity()); + + const int position_id = shader->get_attrib_location("v_position"); + const int normal_id = shader->get_attrib_location("v_normal"); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, cap.vbo)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, buffer->vertices.position_size_floats(), GL_FLOAT, GL_FALSE, buffer->vertices.vertex_size_bytes(), (const void*)buffer->vertices.position_offset_bytes())); + glsafe(::glEnableVertexAttribArray(position_id)); + } + const bool has_normals = buffer->vertices.normal_size_floats() > 0; + if (has_normals) { + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, buffer->vertices.normal_size_floats(), GL_FLOAT, GL_FALSE, buffer->vertices.vertex_size_bytes(), (const void*)buffer->vertices.normal_offset_bytes())); + glsafe(::glEnableVertexAttribArray(normal_id)); } + } - shader->set_uniform("uniform_color", cap.color); + shader->set_uniform("uniform_color", cap.color); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cap.ibo)); - glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)cap.indices_count(), GL_UNSIGNED_SHORT, nullptr)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cap.ibo)); + glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)cap.indices_count(), GL_UNSIGNED_SHORT, nullptr)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - if (has_normals) - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - shader->stop_using(); - } + shader->stop_using(); }; for (unsigned int i = 0; i < 2; ++i) { @@ -4123,23 +4033,16 @@ void GCodeViewer::render_shells() if (shader == nullptr) return; - // when the background processing is enabled, it may happen that the shells data have been loaded - // before opengl has been initialized for the preview canvas. - // when this happens, the volumes' data have not been sent to gpu yet. - for (GLVolume* v : m_shells.volumes.volumes) { - if (!v->indexed_vertex_array.has_VBOs()) - v->finalize_geometry(true); - } - - glsafe(::glEnable(GL_DEPTH_TEST)); -// glsafe(::glDepthMask(GL_FALSE)); + glsafe(::glDepthMask(GL_FALSE)); shader->start_using(); - //BBS: reopen cul faces - m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); + shader->set_uniform("emission_factor", 0.1f); + const Camera& camera = wxGetApp().plater()->get_camera(); + m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, true, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->set_uniform("emission_factor", 0.0f); shader->stop_using(); - // glsafe(::glDepthMask(GL_TRUE)); + glsafe(::glDepthMask(GL_TRUE)); } //BBS @@ -4168,7 +4071,8 @@ void GCodeViewer::render_all_plates_stats(const std::vector filament_diameters = gcode_result_list.front()->filament_diameters; std::vector filament_densities = gcode_result_list.front()->filament_densities; - std::vector filament_colors = decode_colors(wxGetApp().plater()->get_extruder_colors_from_plater_config(gcode_result_list.back())); + std::vector filament_colors; + decode_colors(wxGetApp().plater()->get_extruder_colors_from_plater_config(gcode_result_list.back()), filament_colors); for (int i = 0; i < filament_colors.size(); i++) { filament_colors[i] = adjust_color_for_rendering(filament_colors[i]); @@ -4213,13 +4117,13 @@ void GCodeViewer::render_all_plates_stats(const std::vector>& columns_offsets) + auto append_item = [icon_size, &imgui, imperial_units, &window_padding, &draw_list, this](const ColorRGBA& color, const std::vector>& columns_offsets) { // render icon ImVec2 pos = ImVec2(ImGui::GetCursorScreenPos().x + window_padding * 3, ImGui::GetCursorScreenPos().y); draw_list->AddRectFilled({ pos.x + 1.0f * m_scale, pos.y + 3.0f * m_scale }, { pos.x + icon_size - 1.0f * m_scale, pos.y + icon_size + 1.0f * m_scale }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + ImGuiWrapper::to_ImU32(color)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0 * m_scale, 6.0 * m_scale)); @@ -4425,7 +4329,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv auto append_item = [icon_size, &imgui, imperial_units, &window_padding, &draw_list, this]( EItemType type, - const Color& color, + const ColorRGBA& color, const std::vector>& columns_offsets, bool checkbox = true, bool visible = true, @@ -4437,21 +4341,21 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv default: case EItemType::Rect: { draw_list->AddRectFilled({ pos.x + 1.0f * m_scale, pos.y + 3.0f * m_scale }, { pos.x + icon_size - 1.0f * m_scale, pos.y + icon_size + 1.0f * m_scale }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + ImGuiWrapper::to_ImU32(color)); break; } case EItemType::Circle: { ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size + 5.0f)); - draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGuiWrapper::to_ImU32(color), 16); break; } case EItemType::Hexagon: { ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size + 5.0f)); - draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6); + draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGuiWrapper::to_ImU32(color), 6); break; } case EItemType::Line: { - draw_list->AddLine({ pos.x + 1, pos.y + icon_size + 2 }, { pos.x + icon_size - 1, pos.y + 4 }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); + draw_list->AddLine({ pos.x + 1, pos.y + icon_size + 2 }, { pos.x + icon_size - 1, pos.y + 4 }, ImGuiWrapper::to_ImU32(color), 3.0f); break; case EItemType::None: break; @@ -4567,7 +4471,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv }; auto color_print_ranges = [this](unsigned char extruder_id, const std::vector& custom_gcode_per_print_z) { - std::vector>> ret; + std::vector>> ret; ret.reserve(custom_gcode_per_print_z.size()); for (const auto& item : custom_gcode_per_print_z) { @@ -4587,7 +4491,11 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv // to avoid duplicate values, check adding values if (ret.empty() || !(ret.back().second.first == previous_z && ret.back().second.second == current_z)) - ret.push_back({ decode_color(item.color), { previous_z, current_z } }); + { + ColorRGBA color; + decode_color(item.color, color); + ret.push_back({ color, { previous_z, current_z } }); + } } return ret; @@ -4834,7 +4742,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv } auto append_option_item = [this, append_item](EMoveType type, std::vector offsets) { - auto append_option_item_with_type = [this, offsets, append_item](EMoveType type, const Color& color, const std::string& label, bool visible) { + auto append_option_item_with_type = [this, offsets, append_item](EMoveType type, const ColorRGBA& color, const std::string& label, bool visible) { append_item(EItemType::Rect, color, {{ label , offsets[0] }}, true, visible, [this, type, visible]() { m_buffers[buffer_id(type)].visible = !m_buffers[buffer_id(type)].visible; // update buffers' render paths @@ -4958,7 +4866,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv if (need_scrollable) ImGui::BeginChild("color_prints", { -1.0f, child_height }, false); if (m_extruder_ids.size() == 1) { // single extruder use case - const std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); + const std::vector>> cp_values = color_print_ranges(0, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); auto extruder_idx = m_extruder_ids[0]; if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode @@ -4990,7 +4898,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv // shows only extruders actually used size_t i = 0; for (auto extruder_idx : m_extruder_ids) { - const std::vector>> cp_values = color_print_ranges(extruder_idx, custom_gcode_per_print_z); + const std::vector>> cp_values = color_print_ranges(extruder_idx, custom_gcode_per_print_z); const int items_cnt = static_cast(cp_values.size()); if (items_cnt == 0) { // There are no color changes, but there are some pause print or custom Gcode const bool filament_visible = m_tools.m_tool_visibles[extruder_idx]; @@ -5119,8 +5027,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv }; EType type; int extruder_id; - Color color1; - Color color2; + ColorRGBA color1; + ColorRGBA color2; Times times; std::pair used_filament {0.0f, 0.0f}; }; @@ -5131,7 +5039,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv //BBS: replace model custom gcode with current plate custom gcode std::vector custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : m_custom_gcode_per_print_z; - std::vector last_color(m_extruders_count); + std::vector last_color(m_extruders_count); for (size_t i = 0; i < m_extruders_count; ++i) { last_color[i] = m_tools.m_tool_colors[i]; } @@ -5143,8 +5051,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv case CustomGCode::PausePrint: { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second }); - items.push_back({ PartialTime::EType::Pause, it->extruder, Color(), Color(), time_rec.second }); + items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], ColorRGBA::BLACK(), time_rec.second }); + items.push_back({ PartialTime::EType::Pause, it->extruder, ColorRGBA::BLACK(), ColorRGBA::BLACK(), time_rec.second }); custom_gcode_per_print_z.erase(it); } break; @@ -5152,14 +5060,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv case CustomGCode::ColorChange: { auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; }); if (it != custom_gcode_per_print_z.end()) { - items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder-1) }); - items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], decode_color(it->color), time_rec.second }); - last_color[it->extruder - 1] = decode_color(it->color); + items.push_back({ PartialTime::EType::Print, it->extruder, last_color[it->extruder - 1], ColorRGBA::BLACK(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], it->extruder - 1) }); + ColorRGBA color; + decode_color(it->color, color); + items.push_back({ PartialTime::EType::ColorChange, it->extruder, last_color[it->extruder - 1], color, time_rec.second }); + last_color[it->extruder - 1] = color; last_extruder_id = it->extruder; custom_gcode_per_print_z.erase(it); } else - items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], Color(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id -1) }); + items.push_back({ PartialTime::EType::Print, last_extruder_id, last_color[last_extruder_id - 1], ColorRGBA::BLACK(), time_rec.second, get_used_filament_from_volume(used_filaments[color_change_idx++], last_extruder_id - 1) }); break; } @@ -5170,7 +5080,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv return items; }; - auto append_color_change = [&imgui](const Color& color1, const Color& color2, const std::array& offsets, const Times& times) { + auto append_color_change = [&imgui](const ColorRGBA& color1, const ColorRGBA& color2, const std::array& offsets, const Times& times) { imgui.text(_u8L("Color change")); ImGui::SameLine(); @@ -5180,16 +5090,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color1[0], color1[1], color1[2], 1.0f })); + ImGuiWrapper::to_ImU32(color1)); pos.x += icon_size; draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color2[0], color2[1], color2[2], 1.0f })); + ImGuiWrapper::to_ImU32(color2)); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(times.second - times.first))); }; - auto append_print = [&imgui, imperial_units](const Color& color, const std::array& offsets, const Times& times, std::pair used_filament) { + 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::SameLine(); @@ -5199,7 +5109,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x; draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f }, - ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f })); + ImGuiWrapper::to_ImU32(color)); ImGui::SameLine(offsets[0]); imgui.text(short_time(get_time_dhms(times.second))); @@ -5419,6 +5329,74 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv } } } + // Custom g-code overview + std::vector custom_gcode_per_print_z = wxGetApp().is_editor() ? + wxGetApp().plater()->model().get_curr_plate_custom_gcodes().gcodes : + m_custom_gcode_per_print_z; + if (custom_gcode_per_print_z.size() != 0) { + float max_len = window_padding + 2 * ImGui::GetStyle().ItemSpacing.x; + ImGui::Spacing(); + // Title Line + std::string cgcode_title_str = _u8L("Custom g-code"); + std::string cgcode_layer_str = _u8L("Layer"); + std::string cgcode_time_str = _u8L("Time"); + // Types of custom gcode + std::string cgcode_pause_str = _u8L("Pause"); + std::string cgcode_template_str= _u8L("Template"); + std::string cgcode_toolchange_str = _u8L("ToolChange"); + std::string cgcode_custom_str = _u8L("Custom"); + std::string cgcode_unknown_str = _u8L("Unknown"); + + // Get longest String + max_len += std::max(ImGui::CalcTextSize(cgcode_title_str.c_str()).x, + std::max(ImGui::CalcTextSize(cgcode_pause_str.c_str()).x, + std::max(ImGui::CalcTextSize(cgcode_template_str.c_str()).x, + std::max(ImGui::CalcTextSize(cgcode_toolchange_str.c_str()).x, + std::max(ImGui::CalcTextSize(cgcode_custom_str.c_str()).x, + ImGui::CalcTextSize(cgcode_unknown_str.c_str()).x)))) + + ); + + ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1)); + ImGui::Dummy({window_padding, window_padding}); + ImGui::SameLine(); + imgui.title(cgcode_title_str,true); + ImGui::SameLine(max_len); + imgui.title(cgcode_layer_str, true); + ImGui::SameLine(max_len*1.5); + imgui.title(cgcode_time_str, false); + + for (Slic3r::CustomGCode::Item custom_gcode : custom_gcode_per_print_z) { + ImGui::Dummy({window_padding, window_padding}); + ImGui::SameLine(); + + switch (custom_gcode.type) { + case PausePrint: imgui.text(cgcode_pause_str); break; + case Template: imgui.text(cgcode_template_str); break; + case ToolChange: imgui.text(cgcode_toolchange_str); break; + case Custom: imgui.text(cgcode_custom_str); break; + default: imgui.text(cgcode_unknown_str); break; + } + ImGui::SameLine(max_len); + char buf[64]; + int layer = m_layers.get_l_at(custom_gcode.print_z); + ::sprintf(buf, "%d",layer ); + imgui.text(buf); + ImGui::SameLine(max_len * 1.5); + + std::vector layer_times = m_print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].layers_times; + float custom_gcode_time = 0; + if (layer > 0) + { + for (int i = 0; i < layer-1; i++) { + custom_gcode_time += layer_times[i]; + } + } + imgui.text(short_time(get_time_dhms(custom_gcode_time))); + + } + } + // total estimated printing time section if (show_estimated) { @@ -5496,10 +5474,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv imgui.text(buf); } - auto role_time = [time_mode](ExtrusionRole role) { + auto role_time = [time_mode](ExtrusionRole role) { auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); - return (it != time_mode.roles_times.end()) ? it->second : 0.0f; - }; + return (it != time_mode.roles_times.end()) ? it->second : 0.0f; + }; //BBS: start gcode is mostly same with prepeare time if (time_mode.prepare_time != 0.0f) { ImGui::Dummy({ window_padding, window_padding }); @@ -5595,13 +5573,13 @@ void GCodeViewer::render_statistics() ImGuiWrapper& imgui = *wxGetApp().imgui(); - auto add_time = [this, &imgui](const std::string& label, int64_t time) { + auto add_time = [&imgui](const std::string& label, int64_t time) { imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); imgui.text(std::to_string(time) + " ms (" + get_time_dhms(static_cast(time) * 0.001f) + ")"); }; - auto add_memory = [this, &imgui](const std::string& label, int64_t memory) { + auto add_memory = [&imgui](const std::string& label, int64_t memory) { auto format_string = [memory](const std::string& units, float value) { return std::to_string(memory) + " bytes (" + Slic3r::float_to_string_decimal_point(float(memory) * value, 3) @@ -5624,7 +5602,7 @@ void GCodeViewer::render_statistics() imgui.text(format_string("GB", inv_gb)); }; - auto add_counter = [this, &imgui](const std::string& label, int64_t counter) { + auto add_counter = [&imgui](const std::string& label, int64_t counter) { imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); imgui.text(std::to_string(counter)); @@ -5648,7 +5626,6 @@ void GCodeViewer::render_statistics() } if (ImGui::CollapsingHeader("OpenGL calls")) { - add_counter(std::string("Multi GL_POINTS:"), m_statistics.gl_multi_points_calls_count); add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count); add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); @@ -5713,7 +5690,7 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional) } } -GCodeViewer::Color GCodeViewer::option_color(EMoveType move_type) const +ColorRGBA GCodeViewer::option_color(EMoveType move_type) const { switch (move_type) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 818f3a3e5e..a91e7b938e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -34,7 +34,6 @@ static const float SLIDER_BOTTOM_MARGIN = 64.0f; class GCodeViewer { using IBufferType = unsigned short; - using Color = std::array; using VertexBuffer = std::vector; using MultiVertexBuffer = std::vector; using IndexBuffer = std::vector; @@ -43,12 +42,12 @@ class GCodeViewer using InstanceIdBuffer = std::vector; using InstancesOffsets = std::vector; - static const std::vector Extrusion_Role_Colors; - static const std::vector Options_Colors; - static const std::vector Travel_Colors; - static const std::vector Range_Colors; - static const Color Wipe_Color; - static const Color Neutral_Color; + static const std::vector Extrusion_Role_Colors; + static const std::vector Options_Colors; + static const std::vector Travel_Colors; + static const std::vector Range_Colors; + static const ColorRGBA Wipe_Color; + static const ColorRGBA Neutral_Color; enum class EOptionsColors : unsigned char { @@ -133,7 +132,7 @@ class GCodeViewer // vbo id unsigned int vbo{ 0 }; // Color to apply to the instances - Color color; + ColorRGBA color; }; std::vector ranges; @@ -256,7 +255,7 @@ class GCodeViewer // Index of the parent tbuffer unsigned char tbuffer_id; // Render path property - Color color; + ColorRGBA color; // Index of the buffer in TBuffer::indices unsigned int ibuffer_id; // Render path content @@ -276,12 +275,10 @@ class GCodeViewer bool operator() (const RenderPath &l, const RenderPath &r) const { if (l.tbuffer_id < r.tbuffer_id) return true; - for (int i = 0; i < 3; ++i) { - if (l.color[i] < r.color[i]) - return true; - else if (l.color[i] > r.color[i]) - return false; - } + if (l.color < r.color) + return true; + else if (l.color > r.color) + return false; return l.ibuffer_id < r.ibuffer_id; } }; @@ -296,7 +293,6 @@ class GCodeViewer { enum class ERenderPrimitiveType : unsigned char { - Point, Line, Triangle, InstancedModel, @@ -312,9 +308,9 @@ class GCodeViewer struct Model { GLModel model; - Color color; + ColorRGBA color; InstanceVBuffer instances; - GLModel::InitializationData data; + GLModel::Geometry data; void reset(); }; @@ -337,7 +333,6 @@ class GCodeViewer unsigned int max_vertices_per_segment() const { switch (render_primitive_type) { - case ERenderPrimitiveType::Point: { return 1; } case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 8; } default: { return 0; } @@ -349,7 +344,6 @@ class GCodeViewer unsigned int indices_per_segment() const { switch (render_primitive_type) { - case ERenderPrimitiveType::Point: { return 1; } case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles default: { return 0; } @@ -359,7 +353,6 @@ class GCodeViewer unsigned int max_indices_per_segment() const { switch (render_primitive_type) { - case ERenderPrimitiveType::Point: { return 1; } case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles default: { return 0; } @@ -370,14 +363,13 @@ class GCodeViewer bool has_data() const { switch (render_primitive_type) { - case ERenderPrimitiveType::Point: case ERenderPrimitiveType::Line: case ERenderPrimitiveType::Triangle: { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); } case ERenderPrimitiveType::BatchedModel: { - return model.data.vertices_count() > 0 && model.data.indices_count() && + return !model.data.vertices.empty() && !model.data.indices.empty() && !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } default: { return false; } @@ -416,7 +408,7 @@ class GCodeViewer void reset(bool log = false) { min = FLT_MAX; max = -FLT_MAX; count = 0; log_scale = log; } float step_size() const; - Color get_color_at(float value) const; + ColorRGBA get_color_at(float value) const; float get_value_at_step(int step) const; }; @@ -519,7 +511,7 @@ Range layer_duration_log; TBuffer* buffer{ nullptr }; unsigned int ibo{ 0 }; unsigned int vbo{ 0 }; - Color color; + ColorRGBA color; ~SequentialRangeCap(); bool is_renderable() const { return buffer != nullptr; } @@ -539,7 +531,6 @@ Range layer_duration_log; int64_t refresh_time{ 0 }; int64_t refresh_paths_time{ 0 }; // opengl calls - int64_t gl_multi_points_calls_count{ 0 }; int64_t gl_multi_lines_calls_count{ 0 }; int64_t gl_multi_triangles_calls_count{ 0 }; int64_t gl_triangles_calls_count{ 0 }; @@ -582,7 +573,6 @@ Range layer_duration_log; } void reset_opengl() { - gl_multi_points_calls_count = 0; gl_multi_lines_calls_count = 0; gl_multi_triangles_calls_count = 0; gl_triangles_calls_count = 0; @@ -645,7 +635,7 @@ public: bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } - void render(int canvas_width, int canvas_height, const EViewType& view_type) const; + void render(int canvas_width, int canvas_height, const EViewType& view_type); void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; } void update_curr_move(const GCodeProcessorResult::MoveVertex move); @@ -712,12 +702,12 @@ public: std::vector gcode_ids; float m_scale = 1.0; bool m_show_gcode_window = false; - void render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type) const; + void render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type); }; struct ETools { - std::vector m_tool_colors; + std::vector m_tool_colors; std::vector m_tool_visibles; }; @@ -813,7 +803,7 @@ public: // extract rendering data from the given parameters //BBS: add only gcode mode void load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume, - const std::vector& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode = false); + const std::vector& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode = false); // recalculate ranges in dependence of what is visible and sets tool/print colors void refresh(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors); void refresh_render_paths(); @@ -823,7 +813,7 @@ public: void reset(); //BBS: always load shell at preview void reset_shell(); - void load_shells(const Print& print, bool initialized, bool force_previewing = false); + void load_shells(const Print& print, bool force_previewing = false); void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; } //BBS: add all plates filament statistics void render_all_plates_stats(const std::vector& gcode_result_list, bool show = true) const; @@ -903,7 +893,7 @@ public: private: void load_toolpaths(const GCodeProcessorResult& gcode_result, const BuildVolume& build_volume, const std::vector& exclude_bounding_box); //BBS: always load shell at preview - //void load_shells(const Print& print, bool initialized); + //void load_shells(const Print& print); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void render_toolpaths(); void render_shells(); @@ -920,7 +910,7 @@ private: } bool is_visible(const Path& path) const { return is_visible(path.role); } void log_memory_used(const std::string& label, int64_t additional = 0) const; - Color option_color(EMoveType move_type) const; + ColorRGBA option_color(EMoveType move_type) const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 036e7cbb34..1593b183f7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,3 +1,13 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n +///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill +///|/ Copyright (c) 2020 Benjamin Greiner +///|/ Copyright (c) 2019 John Drake @foxox +///|/ Copyright (c) 2019 BeldrothTheGold @BeldrothTheGold +///|/ Copyright (c) 2019 Thomas Moore +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "GLCanvas3D.hpp" @@ -14,22 +24,16 @@ #include "libslic3r/Technologies.hpp" #include "libslic3r/Tesselate.hpp" #include "libslic3r/PresetBundle.hpp" -#include "slic3r/GUI/3DBed.hpp" -#include "slic3r/GUI/3DScene.hpp" -#include "slic3r/GUI/BackgroundSlicingProcess.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/GLShader.hpp" -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/Tab.hpp" -#include "slic3r/GUI/GUI_Preview.hpp" -#include "slic3r/GUI/OpenGLManager.hpp" -#include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/MainFrame.hpp" -#include "slic3r/Utils/UndoRedo.hpp" -#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp" -#include "slic3r/GUI/BitmapCache.hpp" -#include "slic3r/Utils/MacDarkMode.hpp" - +#include "3DBed.hpp" +#include "3DScene.hpp" +#include "BackgroundSlicingProcess.hpp" +#include "GLShader.hpp" +#include "GUI.hpp" +#include "Tab.hpp" +#include "GUI_Preview.hpp" +#include "OpenGLManager.hpp" +#include "Plater.hpp" +#include "MainFrame.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_Colors.hpp" @@ -38,6 +42,10 @@ #include "NotificationManager.hpp" #include "format.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp" +#include "slic3r/Utils/UndoRedo.hpp" +#include "slic3r/Utils/MacDarkMode.hpp" + #include #if ENABLE_RETINA_GL @@ -78,34 +86,25 @@ static constexpr const float TRACKBALLSIZE = 0.8f; -float GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[3] = { 0.906f, 0.906f, 0.906f }; -float GLCanvas3D::DEFAULT_BG_LIGHT_COLOR_DARK[3] = { 0.329f, 0.329f, 0.353f }; -float GLCanvas3D::ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; -float GLCanvas3D::ERROR_BG_LIGHT_COLOR_DARK[3] = { 0.753f, 0.192f, 0.039f }; +static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f }; +static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR_DARK = { 0.329f, 0.329f, 0.353f, 1.0f }; +static Slic3r::ColorRGBA ERROR_BG_LIGHT_COLOR = { 0.753f, 0.192f, 0.039f, 1.0f }; +static Slic3r::ColorRGBA ERROR_BG_LIGHT_COLOR_DARK = { 0.753f, 0.192f, 0.039f, 1.0f }; void GLCanvas3D::update_render_colors() { - GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[0] = RenderColor::colors[RenderCol_3D_Background].x; - GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[1] = RenderColor::colors[RenderCol_3D_Background].y; - GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[2] = RenderColor::colors[RenderCol_3D_Background].z; + DEFAULT_BG_LIGHT_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_3D_Background]); } void GLCanvas3D::load_render_colors() { - RenderColor::colors[RenderCol_3D_Background] = ImVec4(GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[0], - GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[1], - GLCanvas3D::DEFAULT_BG_LIGHT_COLOR[2], - 1.0f); + RenderColor::colors[RenderCol_3D_Background] = ImGuiWrapper::to_ImVec4(DEFAULT_BG_LIGHT_COLOR); } //static constexpr const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; // Number of floats static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB -// Reserve size in number of floats. -static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB -// Reserve size in number of floats, maximum sum of all preallocated buffers. -//static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB namespace Slic3r { namespace GUI { @@ -229,9 +228,8 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv ImGuiWrapper& imgui = *wxGetApp().imgui(); const Size& cnv_size = canvas.get_canvas_size(); - float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); float left_pos = canvas.m_main_toolbar.get_item("layersediting")->render_left_pos; - const float x = 0.5 * cnv_size.get_width() + left_pos * zoom; + const float x = (1 + left_pos) * cnv_size.get_width() / 2; imgui.set_next_window_pos(x, canvas.m_main_toolbar.get_height(), ImGuiCond_Always, 0.0f, 0.0f); imgui.push_toolbar_style(canvas.get_scale()); @@ -331,9 +329,8 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) { render_variable_layer_height_dialog(canvas); - const Rect& bar_rect = get_bar_rect_viewport(canvas); - render_background_texture(canvas, bar_rect); - render_curve(bar_rect); + render_active_object_annotations(canvas); + render_profile(canvas); } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -367,15 +364,6 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) return { w - thickness_bar_width(canvas), 0.0f, w, h }; } -Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) -{ - const Size& cnv_size = canvas.get_canvas_size(); - float half_w = 0.5f * (float)cnv_size.get_width(); - float half_h = 0.5f * (float)cnv_size.get_height(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - return { (half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom }; -} - bool GLCanvas3D::LayersEditing::is_initialized() const { return wxGetApp().get_shader("variable_layer_height") != nullptr; @@ -407,11 +395,19 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con return ret; } -void GLCanvas3D::LayersEditing::render_background_texture(const GLCanvas3D& canvas, const Rect& bar_rect) +void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas) { if (!m_enabled) return; + const Size cnv_size = canvas.get_canvas_size(); + const float cnv_width = (float)cnv_size.get_width(); + const float cnv_height = (float)cnv_size.get_height(); + if (cnv_width == 0.0f || cnv_height == 0.0f) + return; + + const float cnv_inv_width = 1.0f / cnv_width; + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); if (shader == nullptr) return; @@ -423,59 +419,123 @@ void GLCanvas3D::LayersEditing::render_background_texture(const GLCanvas3D& canv shader->set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); shader->set_uniform("z_cursor_band_width", band_width); shader->set_uniform("object_max_z", m_object_max_z); + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + shader->set_uniform("view_normal_matrix", (Matrix3d)Matrix3d::Identity()); glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); // Render the color bar - const float l = bar_rect.get_left(); - const float r = bar_rect.get_right(); - const float t = bar_rect.get_top(); - const float b = bar_rect.get_bottom(); + if (!m_profile.background.is_initialized() || m_profile.old_canvas_width != cnv_width) { + m_profile.old_canvas_width = cnv_width; + m_profile.background.reset(); - ::glBegin(GL_QUADS); - ::glNormal3f(0.0f, 0.0f, 1.0f); - ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(l, b); - ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(r, b); - ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(r, t); - ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(l, t); - glsafe(::glEnd()); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3T2 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + + // vertices + const float l = 1.0f - 2.0f * THICKNESS_BAR_WIDTH * cnv_inv_width; + const float r = 1.0f; + const float t = 1.0f; + const float b = -1.0f; + init_data.add_vertex(Vec3f(l, b, 0.0f), Vec3f::UnitZ(), Vec2f(0.0f, 0.0f)); + init_data.add_vertex(Vec3f(r, b, 0.0f), Vec3f::UnitZ(), Vec2f(1.0f, 0.0f)); + init_data.add_vertex(Vec3f(r, t, 0.0f), Vec3f::UnitZ(), Vec2f(1.0f, 1.0f)); + init_data.add_vertex(Vec3f(l, t, 0.0f), Vec3f::UnitZ(), Vec2f(0.0f, 1.0f)); + + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + + m_profile.background.init_from(std::move(init_data)); + } + + m_profile.background.render(); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); shader->stop_using(); } -void GLCanvas3D::LayersEditing::render_curve(const Rect & bar_rect) +void GLCanvas3D::LayersEditing::render_profile(const GLCanvas3D& canvas) { if (!m_enabled) return; //FIXME show some kind of legend. + if (!m_slicing_parameters) return; + const Size cnv_size = canvas.get_canvas_size(); + const float cnv_width = (float)cnv_size.get_width(); + const float cnv_height = (float)cnv_size.get_height(); + if (cnv_width == 0.0f || cnv_height == 0.0f) + return; + // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. - const float scale_x = bar_rect.get_width() / float(1.12 * m_slicing_parameters->max_layer_height); - const float scale_y = bar_rect.get_height() / m_object_max_z; - const float x = bar_rect.get_left() + float(m_slicing_parameters->layer_height) * scale_x; + const float scale_x = THICKNESS_BAR_WIDTH / float(1.12 * m_slicing_parameters->max_layer_height); + const float scale_y = cnv_height / m_object_max_z; + + const float cnv_inv_width = 1.0f / cnv_width; + const float cnv_inv_height = 1.0f / cnv_height; // Baseline - glsafe(::glColor3f(0.0f, 0.0f, 0.0f)); - ::glBegin(GL_LINE_STRIP); - ::glVertex2f(x, bar_rect.get_bottom()); - ::glVertex2f(x, bar_rect.get_top()); - glsafe(::glEnd()); + if (!m_profile.baseline.is_initialized() || m_profile.old_layer_height_profile != m_layer_height_profile) { + m_profile.baseline.reset(); - // Curve - glsafe(::glColor3f(0.0f, 0.0f, 1.0f)); - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i < m_layer_height_profile.size(); i += 2) - ::glVertex2f(bar_rect.get_left() + (float)m_layer_height_profile[i + 1] * scale_x, bar_rect.get_bottom() + (float)m_layer_height_profile[i] * scale_y); - glsafe(::glEnd()); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P2}; + init_data.color = ColorRGBA::BLACK(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + const float axis_x = 2.0f * ((cnv_width - THICKNESS_BAR_WIDTH + float(m_slicing_parameters->layer_height) * scale_x) * cnv_inv_width - 0.5f); + init_data.add_vertex(Vec2f(axis_x, -1.0f)); + init_data.add_vertex(Vec2f(axis_x, 1.0f)); + + // indices + init_data.add_line(0, 1); + + m_profile.baseline.init_from(std::move(init_data)); + } + + if (!m_profile.profile.is_initialized() || m_profile.old_layer_height_profile != m_layer_height_profile) { + m_profile.old_layer_height_profile = m_layer_height_profile; + m_profile.profile.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2 }; + init_data.color = ColorRGBA::BLUE(); + init_data.reserve_vertices(m_layer_height_profile.size() / 2); + init_data.reserve_indices(m_layer_height_profile.size() / 2); + + // vertices + indices + for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) { + init_data.add_vertex(Vec2f(2.0f * ((cnv_width - THICKNESS_BAR_WIDTH + float(m_layer_height_profile[i + 1]) * scale_x) * cnv_inv_width - 0.5f), + 2.0f * (float(m_layer_height_profile[i]) * scale_y * cnv_inv_height - 0.5))); + init_data.add_index(i / 2); + } + + m_profile.profile.init_from(std::move(init_data)); + } + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + m_profile.baseline.render(); + m_profile.profile.render(); + shader->stop_using(); + } } -void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D & canvas, const GLVolumeCollection & volumes)//render volume and layer height texture (has mapping relation with each other) +void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes) { assert(this->is_allowed()); assert(this->last_object_id != -1); @@ -499,6 +559,9 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D & canvas, const shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas))); shader->set_uniform("z_cursor_band_width", float(this->band_width)); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + // Initialize the layer height texture mapping. const GLsizei w = (GLsizei)m_layers_texture.width; const GLsizei h = (GLsizei)m_layers_texture.height; @@ -517,6 +580,11 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D & canvas, const shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); shader->set_uniform("object_max_z", 0.0f); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d model_matrix = glvolume->world_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); glvolume->render(); } @@ -619,49 +687,6 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D & canvas) * THICKNESS_BAR_WIDTH; } -Size::Size() - : m_width(0) - , m_height(0) -{ -} - -Size::Size(int width, int height, float scale_factor) - : m_width(width) - , m_height(height) - , m_scale_factor(scale_factor) -{ -} - -int Size::get_width() const -{ - return m_width; -} - -void Size::set_width(int width) -{ - m_width = width; -} - -int Size::get_height() const -{ - return m_height; -} - -void Size::set_height(int height) -{ - m_height = height; -} - -int Size::get_scale_factor() const -{ - return m_scale_factor; -} - -void Size::set_scale_factor(int scale_factor) -{ - m_scale_factor = scale_factor; -} - const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); const int GLCanvas3D::Mouse::Drag::MoveThresholdPx = 5; @@ -879,113 +904,81 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons m_perimeter.reset(); m_fill.reset(); if (!polygons.empty()) { - size_t triangles_count = 0; - for (const Polygon &poly : polygons) { triangles_count += poly.points.size() - 2; } - const size_t vertices_count = 3 * triangles_count; - if (m_render_fill) { - GLModel::InitializationData fill_data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; - entity.color = {0.8f, 0.8f, 1.0f, 0.5f}; - entity.positions.reserve(vertices_count); - entity.normals.reserve(vertices_count); - entity.indices.reserve(vertices_count); + GLModel::Geometry fill_data; + fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + fill_data.color = { 0.8f, 0.8f, 1.0f, 0.5f }; + // vertices + indices const ExPolygons polygons_union = union_ex(polygons); - for (const ExPolygon &poly : polygons_union) { + unsigned int vertices_counter = 0; + for (const ExPolygon& poly : polygons_union) { const std::vector triangulation = triangulate_expolygon_3d(poly); - for (const Vec3d &v : triangulation) { - entity.positions.emplace_back(v.cast() + Vec3f(0.0f, 0.0f, 0.0125f)); // add a small positive z to avoid z-fighting - entity.normals.emplace_back(Vec3f::UnitZ()); - const size_t positions_count = entity.positions.size(); - if (positions_count % 3 == 0) { - entity.indices.emplace_back(positions_count - 3); - entity.indices.emplace_back(positions_count - 2); - entity.indices.emplace_back(positions_count - 1); - } + fill_data.reserve_vertices(fill_data.vertices_count() + triangulation.size()); + fill_data.reserve_indices(fill_data.indices_count() + triangulation.size()); + for (const Vec3d& v : triangulation) { + fill_data.add_vertex((Vec3f)(v.cast() + 0.0125f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting + ++vertices_counter; + if (vertices_counter % 3 == 0) + fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } } - fill_data.entities.emplace_back(entity); - m_fill.init_from(fill_data); + m_fill.init_from(std::move(fill_data)); } - GLModel::InitializationData perimeter_data; - for (const Polygon &poly : polygons) { - GLModel::InitializationData::Entity ent; - ent.type = GLModel::PrimitiveType::LineLoop; - ent.positions.reserve(poly.points.size()); - ent.indices.reserve(poly.points.size()); - unsigned int id_count = 0; - for (const Point &p : poly.points) { - ent.positions.emplace_back(unscale(p.x()), unscale(p.y()), 0.025f); // add a small positive z to avoid z-fighting - ent.normals.emplace_back(Vec3f::UnitZ()); - ent.indices.emplace_back(id_count++); - } - - perimeter_data.entities.emplace_back(ent); - } - - m_perimeter.init_from(perimeter_data); + m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting } //BBS: add the height limit compute logic if (!height_polygons.empty()) { - size_t height_triangles_count = 0; - for (const auto &poly : height_polygons) { height_triangles_count += poly.first.points.size() - 2; } - const size_t height_vertices_count = 3 * height_triangles_count; - - GLModel::InitializationData height_fill_data; - GLModel::InitializationData::Entity height_entity; - height_entity.type = GLModel::PrimitiveType::Triangles; - height_entity.color = {0.8f, 0.8f, 1.0f, 0.5f}; - height_entity.positions.reserve(height_vertices_count); - height_entity.normals.reserve(height_vertices_count); - height_entity.indices.reserve(height_vertices_count); + GLModel::Geometry height_fill_data; + height_fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + height_fill_data.color = {0.8f, 0.8f, 1.0f, 0.5f}; + // vertices + indices + unsigned int vertices_counter = 0; for (const auto &poly : height_polygons) { ExPolygon ex_poly(poly.first); const std::vector height_triangulation = triangulate_expolygon_3d(ex_poly); for (const Vec3d &v : height_triangulation) { - Vec3d point{v.x(), v.y(), poly.second}; - height_entity.positions.emplace_back(point.cast()); - height_entity.normals.emplace_back(Vec3f::UnitZ()); - const size_t height_positions_count = height_entity.positions.size(); - if (height_positions_count % 3 == 0) { - height_entity.indices.emplace_back(height_positions_count - 3); - height_entity.indices.emplace_back(height_positions_count - 2); - height_entity.indices.emplace_back(height_positions_count - 1); - } + Vec3f point{(float) v.x(), (float) v.y(), poly.second}; + height_fill_data.add_vertex(point); + ++vertices_counter; + if (vertices_counter % 3 == 0) + height_fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } } - height_fill_data.entities.emplace_back(height_entity); - m_height_limit.init_from(height_fill_data); + m_height_limit.init_from(std::move(height_fill_data)); } } void GLCanvas3D::SequentialPrintClearance::render() { - std::array FILL_COLOR = { 0.7f, 0.7f, 1.0f, 0.5f }; - std::array NO_FILL_COLOR = { 0.75f, 0.75f, 0.75f, 0.75f }; + const ColorRGBA FILL_COLOR = { 0.7f, 0.7f, 1.0f, 0.5f }; + const ColorRGBA NO_FILL_COLOR = { 0.75f, 0.75f, 0.75f, 0.75f }; - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - m_perimeter.set_color(-1, m_render_fill ? FILL_COLOR : NO_FILL_COLOR); + m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR); m_perimeter.render(); m_fill.render(); //BBS: add height limit - m_height_limit.set_color(-1, m_render_fill ? FILL_COLOR : NO_FILL_COLOR); + m_height_limit.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR); m_height_limit.render(); glsafe(::glDisable(GL_BLEND)); @@ -1141,11 +1134,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_moving(false) , m_tab_down(false) , m_cursor_type(Standard) - , m_color_by("volume") , m_reload_delayed(false) -#if ENABLE_RENDER_PICKING_PASS - , m_show_picking_texture(false) -#endif // ENABLE_RENDER_PICKING_PASS , m_render_sla_auxiliaries(true) , m_labels(*this) , m_slope(m_volumes) @@ -1199,36 +1188,6 @@ bool GLCanvas3D::init() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - // Set antialiasing / multisampling - glsafe(::glDisable(GL_LINE_SMOOTH)); - glsafe(::glDisable(GL_POLYGON_SMOOTH)); - - // ambient lighting - GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; - glsafe(::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient)); - - glsafe(::glEnable(GL_LIGHT0)); - glsafe(::glEnable(GL_LIGHT1)); - - // light from camera - GLfloat specular_cam[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; - glsafe(::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam)); - GLfloat diffuse_cam[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - glsafe(::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam)); - - // light from above - GLfloat specular_top[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - glsafe(::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top)); - GLfloat diffuse_top[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; - glsafe(::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top)); - - // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. - glsafe(::glShadeModel(GL_SMOOTH)); - - // A handy trick -- have surface material mirror the color. - glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)); - glsafe(::glEnable(GL_COLOR_MATERIAL)); - if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); @@ -1236,10 +1195,6 @@ bool GLCanvas3D::init() if (m_main_toolbar.is_enabled()) m_layers_editing.init(); - // on linux the gl context is not valid until the canvas is not shown on screen - // we defer the geometry finalization of volumes until the first call to render() - m_volumes.finalize_geometry(true); - BOOST_LOG_TRIVIAL(info) <<__FUNCTION__<< ": before gizmo init"; if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; @@ -1386,21 +1341,31 @@ ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { + if (current_printer_technology() != ptSLA) + return; + m_render_sla_auxiliaries = visible; + std::vector>* raycasters = get_raycasters_for_picking(SceneRaycaster::EType::Volume); + for (GLVolume* vol : m_volumes.volumes) { if (vol->composite_id.object_id >= 1000 && vol->composite_id.object_id < 1000 + wxGetApp().plater()->get_partplate_list().get_plate_count()) continue; // the wipe tower - if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) - && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) - && vol->composite_id.volume_id < 0) + if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) + && vol->composite_id.volume_id < 0) { vol->is_active = visible; + auto it = std::find_if(raycasters->begin(), raycasters->end(), [vol](std::shared_ptr item) { return item->get_raycaster() == vol->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(vol->is_active); + } } } void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx, const ModelVolume* mv) { + std::vector>* raycasters = get_raycasters_for_picking(SceneRaycaster::EType::Volume); for (GLVolume* vol : m_volumes.volumes) { // BBS: add partplate logic if (vol->composite_id.object_id >= 1000 && @@ -1412,6 +1377,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) && (mv == nullptr || m_model->objects[vol->composite_id.object_id]->volumes[vol->composite_id.volume_id] == mv)) { vol->is_active = visible; + if (!vol->is_modifier) + vol->color.a(1.f); if (instance_idx == -1) { vol->force_native_color = false; @@ -1419,10 +1386,12 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject } else { const GLGizmosManager& gm = get_gizmos_manager(); auto gizmo_type = gm.get_current_type(); - if ( (gizmo_type == GLGizmosManager::FdmSupports - || gizmo_type == GLGizmosManager::Seam) - && ! vol->is_modifier) + if ( (gizmo_type == GLGizmosManager::FdmSupports + || gizmo_type == GLGizmosManager::Seam + || gizmo_type == GLGizmosManager::Cut) + && !vol->is_modifier) { vol->force_neutral_color = true; + } else if (gizmo_type == GLGizmosManager::MmuSegmentation) vol->is_active = false; else @@ -1430,7 +1399,12 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject } } } + + auto it = std::find_if(raycasters->begin(), raycasters->end(), [vol](std::shared_ptr item) { return item->get_raycaster() == vol->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(vol->is_active); } + if (visible && !mo) toggle_sla_auxiliaries_visibility(true, mo, instance_idx); @@ -1498,11 +1472,6 @@ Camera& GLCanvas3D::get_camera() return camera; } -void GLCanvas3D::set_color_by(const std::string& value) -{ - m_color_by = value; -} - void GLCanvas3D::refresh_camera_scene_box() { wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box()); @@ -1847,7 +1816,8 @@ void GLCanvas3D::render(bool only_init) // and the viewport was set incorrectly, leading to tripping glAsserts further down // the road (in apply_projection). That's why the minimum size is forced to 10. Camera& camera = wxGetApp().plater()->get_camera(); - camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); + camera.set_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); + camera.apply_viewport(); if (camera.requires_zoom_to_bed) { zoom_to_bed(); @@ -1867,14 +1837,8 @@ void GLCanvas3D::render(bool only_init) camera.requires_zoom_to_volumes = false; } - camera.apply_view_matrix(); camera.apply_projection(_max_bounding_box(true, true, true)); - GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; - glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam)); - GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; - glsafe(::glLightfv(GL_LIGHT0, GL_POSITION, position_top)); - wxGetApp().imgui()->new_frame(); if (m_picking_enabled) { @@ -1883,14 +1847,19 @@ void GLCanvas3D::render(bool only_init) _rectangular_selection_picking_pass(); //BBS: enable picking when no volumes for partplate logic //else if (!m_volumes.empty()) - else + else { // regular picking pass _picking_pass(); + +#if ENABLE_RAYCAST_PICKING_DEBUG + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize); + imgui.text("Picking disabled"); + imgui.end(); +#endif // ENABLE_RAYCAST_PICKING_DEBUG + } } -#if ENABLE_RENDER_PICKING_PASS - if (!m_picking_enabled || !m_show_picking_texture) { -#endif // ENABLE_RENDER_PICKING_PASS // draw scene glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); _render_background(); @@ -1913,9 +1882,9 @@ void GLCanvas3D::render(bool only_init) _render_sla_slices(); _render_selection(); if (!no_partplate) - _render_bed(!camera.is_looking_downward(), show_axes); + _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes); if (!no_partplate) //BBS: add outline logic - _render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true); + _render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true); _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); } /* preview render */ @@ -1923,8 +1892,8 @@ void GLCanvas3D::render(bool only_init) _render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running()); _render_sla_slices(); _render_selection(); - _render_bed(!camera.is_looking_downward(), show_axes); - _render_platelist(!camera.is_looking_downward(), only_current, true, hover_id); + _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes); + _render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, true, hover_id); // BBS: GUI refactor: add canvas size as parameters _render_gcode(cnv_size.get_width(), cnv_size.get_height()); } @@ -1932,7 +1901,7 @@ void GLCanvas3D::render(bool only_init) else if (m_canvas_type == ECanvasType::CanvasAssembleView) { //BBS: add outline logic _render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running()); - //_render_bed(!camera.is_looking_downward(), show_axes); + //_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes); _render_plane(); //BBS: add outline logic insteadof selection under assemble view //_render_selection(); @@ -1955,9 +1924,11 @@ void GLCanvas3D::render(bool only_init) // could be invalidated by the following gizmo render methods _render_selection_sidebar_hints(); _render_current_gizmo(); -#if ENABLE_RENDER_PICKING_PASS - } -#endif // ENABLE_RENDER_PICKING_PASS + +#if ENABLE_RAYCAST_PICKING_DEBUG + if (m_picking_enabled && !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging()) + m_scene_raycaster.render_hit(camera); +#endif // ENABLE_RAYCAST_PICKING_DEBUG #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); @@ -2068,7 +2039,7 @@ void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, { GLShaderProgram* shader = wxGetApp().get_shader("thumbnail"); ModelObjectPtrs& model_objects = GUI::wxGetApp().model().objects; - std::vector> colors = ::get_extruders_colors(); + std::vector colors = ::get_extruders_colors(); switch (OpenGLManager::get_framebuffers_type()) { case OpenGLManager::EFramebufferType::Arb: @@ -2130,8 +2101,8 @@ void GLCanvas3D::set_selected_visible(bool visible) for (unsigned int i : m_selection.get_volume_idxs()) { GLVolume* volume = const_cast(m_selection.get_volume(i)); volume->visible = visible; - volume->color[3] = visible ? 1.f : GLVolume::MODEL_HIDDEN_COL[3]; - volume->render_color[3] = volume->color[3]; + volume->color.a(visible ? 1.f : GLVolume::MODEL_HIDDEN_COL.a()); + volume->render_color.a(volume->color.a()); volume->force_native_color = !visible; } } @@ -2202,7 +2173,7 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.emplace_back(i); } } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs); } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2237,7 +2208,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (!m_initialized) return; - + _set_current(); m_hover_volume_idxs.clear(); @@ -2513,7 +2484,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh // later in this function. it->volume_idx = m_volumes.volumes.size(); - m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized, m_canvas_type == ECanvasType::CanvasAssembleView); + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_canvas_type == ECanvasType::CanvasAssembleView); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { @@ -2570,31 +2541,35 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re GLVolume &volume = *m_volumes.volumes[it->volume_idx]; if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. - volume.indexed_vertex_array.release_geometry(); - if (state.step[istep].state == PrintStateBase::DONE) { + volume.model.reset(); + if (state.step[istep].state == PrintStateBase::DONE) { TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); assert(! mesh.empty()); mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); #if ENABLE_SMOOTH_NORMALS - volume.indexed_vertex_array.load_mesh(mesh, true); + volume.model.init_from(mesh, true); #else - volume.indexed_vertex_array.load_mesh(mesh); -#endif // ENABLE_SMOOTH_NORMALS - } else { - // Reload the original volume. -#if ENABLE_SMOOTH_NORMALS - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#else - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); + volume.model.init_from(mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +#endif // ENABLE_SMOOTH_NORMALS + } + else { + // Reload the original volume. +#if ENABLE_SMOOTH_NORMALS + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#else + const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); + volume.model.init_from(new_mesh); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); #endif // ENABLE_SMOOTH_NORMALS } - volume.finalize_geometry(true); } //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables // of various concenrs (model vs. 3D print path). volume.offsets = { state.step[istep].timestamp }; - } else if (state.step[istep].state == PrintStateBase::DONE) { + } + else if (state.step[istep].state == PrintStateBase::DONE) { // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); @@ -2606,7 +2581,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); else shift_zs[object_idx] = 0.; - } else { + } + else { // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); @@ -2616,7 +2592,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) - m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2680,7 +2656,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( 1000 + plate_id, x + plate_origin(0), y + plate_origin(1), (float)wipe_tower_size(0), (float)wipe_tower_size(1), (float)wipe_tower_size(2), a, - /*!print->is_step_done(psWipeTower)*/ true, brim_width, m_initialized); + /*!print->is_step_done(psWipeTower)*/ true, brim_width); int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id]; if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; @@ -2695,6 +2671,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re else m_selection.volumes_changed(map_glvolume_old_to_new); + // @Enrico suggest this solution to preven accessing pointer on caster without data + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Bed); + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume); m_gizmos.update_data(); m_gizmos.update_assemble_view_data(); m_gizmos.refresh_on_off_state(); @@ -2702,9 +2681,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Update the toolbar //BBS: notify the PartPlateList to reload all objects if (update_object_list) - { post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - } //BBS:exclude the assmble view if (m_canvas_type != ECanvasType::CanvasAssembleView) { @@ -2749,30 +2726,37 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #endif } + // refresh bed raycasters for picking + if (m_canvas_type == ECanvasType::CanvasView3D) { + wxGetApp().plater()->get_partplate_list().register_raycasters_for_picking(*this); + } + + // refresh volume raycasters for picking + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + assert(v->mesh_raycaster != nullptr); + std::shared_ptr raycaster = add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *v->mesh_raycaster, v->world_matrix()); + raycaster->set_active(v->is_active); + } + + // refresh gizmo elements raycasters for picking + GLGizmoBase* curr_gizmo = m_gizmos.get_current(); + if (curr_gizmo != nullptr) + curr_gizmo->unregister_raycasters_for_picking(); + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo); + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::FallbackGizmo); + if (curr_gizmo != nullptr && !m_selection.is_empty()) + curr_gizmo->register_raycasters_for_picking(); + // and force this canvas to be redrawn. m_dirty = true; } -static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE) -{ - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol_old.indexed_vertex_array = vol_new.indexed_vertex_array; - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - // Reserving number of vertices (3x position + 3x color) - vol_new.indexed_vertex_array.reserve(prealloc_size / 6); - // Finalize the old geometry, possibly move data to the graphics card. - vol_old.finalize_geometry(gl_initialized); -} - void GLCanvas3D::load_shells(const Print& print, bool force_previewing) { if (m_initialized) { - m_gcode_viewer.load_shells(print, m_initialized, force_previewing); + m_gcode_viewer.load_shells(print, force_previewing); m_gcode_viewer.update_shells_color_by_extruder(m_config); } } @@ -2792,7 +2776,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co //when load gcode directly, it is too late m_gcode_viewer.init(wxGetApp().get_mode(), wxGetApp().preset_bundle); m_gcode_viewer.load(gcode_result, *this->fff_print(), wxGetApp().plater()->build_volume(), exclude_bounding_box, - m_initialized, wxGetApp().get_mode(), only_gcode); + wxGetApp().get_mode(), only_gcode); if (wxGetApp().is_editor()) { //BBS: always load shell at preview, do this in load_shells @@ -3236,14 +3220,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) //} //case 'O': //case 'o': { _update_camera_zoom(-1.0); break; } -#if ENABLE_RENDER_PICKING_PASS - case 'T': - case 't': { - m_show_picking_texture = !m_show_picking_texture; - m_dirty = true; - break; - } -#endif // ENABLE_RENDER_PICKING_PASS //case 'Z': //case 'z': { // if (!m_selection.is_empty()) @@ -3369,7 +3345,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) m_dirty = true; }, [this](const Vec3d& direction, bool slow, bool camera_space) { - m_selection.start_dragging(); + m_selection.setup_cache(); double multiplier = slow ? 1.0 : 10.0; Vec3d displacement; @@ -3382,7 +3358,6 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) displacement = multiplier * direction; m_selection.translate(displacement); - m_selection.stop_dragging(); m_dirty = true; } );} @@ -3503,14 +3478,14 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) if (keyCode == WXK_SHIFT) { translationProcessor.process(evt); - if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) + if (m_picking_enabled /*&& (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)*/) { m_mouse.ignore_left_up = false; // set_cursor(Cross); } } else if (keyCode == WXK_ALT) { - if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) + if (m_picking_enabled /*&& (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)*/) { m_mouse.ignore_left_up = false; // set_cursor(Cross); @@ -3523,9 +3498,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) post_event(SimpleEvent(EVT_GLCANVAS_COLLAPSE_SIDEBAR)); } else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { auto _do_rotate = [this](double angle_z_rad) { - m_selection.start_dragging(); + m_selection.setup_cache(); m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); - m_selection.stop_dragging(); m_dirty = true; // wxGetApp().obj_manipul()->set_dirty(); }; @@ -3960,30 +3934,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_start_position_3D_as_invalid(); m_mouse.position = pos.cast(); - if (evt.Dragging() && current_printer_technology() == ptFFF && (fff_print()->config().print_sequence == PrintSequence::ByObject)) { - switch (m_gizmos.get_current_type()) - { - case GLGizmosManager::EType::Move: - case GLGizmosManager::EType::Scale: - case GLGizmosManager::EType::Rotate: - { - update_sequential_clearance(); - break; - } - default: { break; } - } - } - else if (evt.Dragging()) { - switch (m_gizmos.get_current_type()) - { - case GLGizmosManager::EType::Move: - case GLGizmosManager::EType::Scale: - case GLGizmosManager::EType::Rotate: - { - show_sinking_contours(); - break; - } - default: { break; } + // It should be detection of volume change + // Not only detection of some modifiers !!! + if (evt.Dragging()) { + GLGizmosManager::EType c = m_gizmos.get_current_type(); + if (current_printer_technology() == ptFFF && + (fff_print()->config().print_sequence == PrintSequence::ByObject)) { + if (c == GLGizmosManager::EType::Move || + c == GLGizmosManager::EType::Scale || + c == GLGizmosManager::EType::Rotate ) + update_sequential_clearance(); + } else { + if (c == GLGizmosManager::EType::Move || + c == GLGizmosManager::EType::Scale || + c == GLGizmosManager::EType::Rotate) + show_sinking_contours(); } } else if (evt.LeftUp() && @@ -4060,9 +4025,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // BBS: define Alt key to enable volume selection mode m_selection.set_volume_selection_mode(evt.AltDown() ? Selection::Volume : Selection::Instance); if (evt.LeftDown() && evt.ShiftDown() && m_picking_enabled && m_layers_editing.state != LayersEditing::Editing) { - if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports - && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports + if (/*m_gizmos.get_current_type() != GLGizmosManager::SlaSupports + &&*/ m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::Seam + && m_gizmos.get_current_type() != GLGizmosManager::Cut && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_dirty = true; @@ -4112,11 +4078,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int volume_idx = get_first_hover_volume_idx(); BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); volume_bbox.offset(1.0); - if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) { + const bool is_cut_connector_selected = m_selection.is_any_connector(); + if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position) && !is_cut_connector_selected) { m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None; // The dragging operation is initiated. m_mouse.drag.move_volume_idx = volume_idx; - m_selection.start_dragging(); + m_selection.setup_cache(); m_mouse.drag.start_position_3D = m_mouse.scene_position; m_sequential_print_clearance_first_displacement = true; m_moving = true; @@ -4281,8 +4248,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { + m_mouse.position = pos.cast(); + if (evt.LeftUp()) { - m_selection.stop_dragging(); m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f; } @@ -4323,7 +4291,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) deselect_all(); } else if (evt.RightUp() && !is_layers_editing_enabled()) { - m_mouse.position = pos.cast(); // forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while // the context menu is already shown render(); @@ -4331,7 +4298,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // if right clicking on volume, propagate event through callback (shows context menu) int volume_idx = get_first_hover_volume_idx(); if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower - && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open + /*&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports*/) // disable context menu when the gizmo is open { // forces the selection of the volume /* m_selection.add(volume_idx); // #et_FIXME_if_needed @@ -4576,12 +4543,13 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { const ModelObject* obj = m_model->objects[i]; for (int j = 0; j < static_cast(obj->instances.size()); ++j) { - if (snapshot_type.empty() && m_selection.get_object_idx() == i) { + if (snapshot_type == L("Gizmo-Place on Face") && m_selection.get_object_idx() == i) { // This means we are flattening this object. In that case pretend // that it is not sinking (even if it is), so it is placed on bed // later on (whatever is sinking will be left sinking). min_zs[{ i, j }] = SINKING_Z_THRESHOLD; - } else + } + else min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); } @@ -4732,15 +4700,6 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) m_dirty = true; } -void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_type) -{ - if (!snapshot_type.empty()) - wxGetApp().plater()->take_snapshot(snapshot_type); - - m_selection.flattening_rotate(normal); - do_rotate(""); // avoid taking another snapshot -} - void GLCanvas3D::do_center() { if (m_model == nullptr) @@ -5267,7 +5226,7 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa //original use center as {0.0}, and top is (canvas_h/2), bottom is (-canvas_h/2), also plus inv_camera //now change to left_up as {0,0}, and top is 0, bottom is canvas_h #if BBS_TOOLBAR_ON_TOP - const float x = left * float(wxGetApp().plater()->get_camera().get_zoom()) + 0.5f * canvas_w; + const float x = (1 + left) * canvas_w / 2; ImGuiWrapper::push_toolbar_style(get_scale()); imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); #else @@ -5352,9 +5311,8 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo //original use center as {0.0}, and top is (canvas_h/2), bottom is (-canvas_h/2), also plus inv_camera //now change to left_up as {0,0}, and top is 0, bottom is canvas_h #if BBS_TOOLBAR_ON_TOP - float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); float left_pos = m_main_toolbar.get_item("arrange")->render_left_pos; - const float x = 0.5 * canvas_w + left_pos * zoom; + const float x = (1 + left_pos) * canvas_w / 2; imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.0f, 0.0f); #else @@ -5526,7 +5484,7 @@ static void debug_output_thumbnail(const ThumbnailData& thumbnail_data) #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, - PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { //BBS modify visible calc function @@ -5563,9 +5521,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const return ret; }; - static std::array curr_color; - static const std::array orange = { 0.923f, 0.504f, 0.264f, 1.0f }; - static const std::array gray = { 0.64f, 0.64f, 0.64f, 1.0f }; + static ColorRGBA curr_color; GLVolumePtrs visible_volumes; @@ -5603,7 +5559,8 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const //BBS modify scene box to plate scene bounding box //plate_build_volume.min(2) = - plate_build_volume.max(2); camera.set_scene_box(plate_build_volume); - camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.set_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.apply_viewport(); //BoundingBoxf3 plate_box = plate->get_bounding_box(false); //plate_box.min.z() = 0.0; @@ -5625,15 +5582,10 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const } else { camera.zoom_to_box(volumes_box); - const Vec3d& target = camera.get_target(); - double distance = camera.get_distance(); camera.select_view("iso"); - camera.apply_view_matrix(); - - camera.apply_projection(plate_build_volume); } - camera.apply_view_matrix(); + const Transform3d &view_matrix = camera.get_view_matrix(); camera.apply_projection(plate_build_volume); @@ -5650,6 +5602,8 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); + const Transform3d &projection_matrix = camera.get_projection_matrix(); + if (for_picking) { //if (OpenGLManager::can_multisample()) // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. @@ -5662,9 +5616,6 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const // do not cull backfaces to show broken geometry, if any glsafe(::glDisable(GL_CULL_FACE)); - //glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - //glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - for (GLVolume* vol : visible_volumes) { // Object picking mode. Render the object with a color encoding the object index. // we reserve color = (0,0,0) for occluders (as the printbed) @@ -5676,16 +5627,21 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const unsigned int g = (id & (0x000000FF << 8)) >> 8; unsigned int b = (id & (0x000000FF << 16)) >> 16; unsigned int a = 0xFF; - glsafe(::glColor4f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255)); + vol->model.set_color({(GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255}); /*curr_color[0] = (GLfloat)r * INV_255; curr_color[1] = (GLfloat)g * INV_255; curr_color[2] = (GLfloat)b * INV_255; curr_color[3] = (GLfloat)a * INV_255; shader->set_uniform("uniform_color", curr_color);*/ - bool is_active = vol->is_active; + const bool is_active = vol->is_active; vol->is_active = true; - vol->simple_render(nullptr, model_objects, extruder_colors); + const Transform3d model_matrix = vol->world_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + vol->simple_render(shader, model_objects, extruder_colors); vol->is_active = is_active; } @@ -5702,13 +5658,10 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const shader->set_uniform("emission_factor", 0.1f); for (GLVolume* vol : visible_volumes) { //BBS set render color for thumbnails - curr_color[0] = vol->color[0]; - curr_color[1] = vol->color[1]; - curr_color[2] = vol->color[2]; - curr_color[3] = vol->color[3]; + curr_color = vol->color; - std::array new_color = adjust_color_for_rendering(curr_color); - shader->set_uniform("uniform_color", new_color); + ColorRGBA new_color = adjust_color_for_rendering(curr_color); + vol->model.set_color(new_color); shader->set_uniform("volume_world_matrix", vol->world_matrix()); //BBS set all volume to orange //shader->set_uniform("uniform_color", orange); @@ -5719,8 +5672,13 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); }*/ // the volume may have been deactivated by an active gizmo - bool is_active = vol->is_active; + const bool is_active = vol->is_active; vol->is_active = true; + const Transform3d model_matrix = vol->world_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); vol->simple_render(shader, model_objects, extruder_colors); vol->is_active = is_active; } @@ -5739,7 +5697,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const } void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, - PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { thumbnail_data.set(w, h); @@ -5847,7 +5805,7 @@ void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, uns } void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, - PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { thumbnail_data.set(w, h); @@ -5949,7 +5907,7 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, // glsafe(::glDisable(GL_MULTISAMPLE)); } -void GLCanvas3D::render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList &partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList &partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type) { // check that thumbnail size does not exceed the default framebuffer size const Size& cnv_size = get_canvas_size(); @@ -5973,7 +5931,7 @@ void GLCanvas3D::render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT // restore the default framebuffer size to avoid flickering on the 3D scene - //wxGetApp().plater()->get_camera().apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); + //wxGetApp().plater()->get_camera().apply_viewport(); } //BBS: GUI refractor @@ -6075,21 +6033,12 @@ bool GLCanvas3D::_init_main_toolbar() return true; } // init arrow - BackgroundTexture::Metadata arrow_data; - arrow_data.filename = "toolbar_arrow.svg"; - arrow_data.left = 0; - arrow_data.top = 0; - arrow_data.right = 0; - arrow_data.bottom = 0; - if (!m_main_toolbar.init_arrow(arrow_data)) - { + if (!m_main_toolbar.init_arrow("toolbar_arrow.svg")) BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture."; - } + // m_gizmos is created at constructor, thus we can init arrow here. - if (!m_gizmos.init_arrow(arrow_data)) - { + if (!m_gizmos.init_arrow("toolbar_arrow.svg")) BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture."; - } m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); //BBS: main toolbar is at the top and left, we don't need the rounded-corner effect at the right side and the top side @@ -6474,92 +6423,176 @@ void GLCanvas3D::_refresh_if_shown_on_screen() void GLCanvas3D::_picking_pass() { - std::vector* hover_volume_idxs = const_cast*>(&m_hover_volume_idxs); - std::vector* hover_plate_idxs = const_cast*>(&m_hover_plate_idxs); + if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX) || m_gizmos.is_dragging()) { +#if ENABLE_RAYCAST_PICKING_DEBUG + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize); + imgui.text("Picking disabled"); + imgui.end(); +#endif // ENABLE_RAYCAST_PICKING_DEBUG + return; + } - if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) { - hover_volume_idxs->clear(); - hover_plate_idxs->clear(); + m_hover_volume_idxs.clear(); + m_hover_plate_idxs.clear(); - // Render the object for picking. - // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. - // Better to use software ray - casting on a bounding - box hierarchy. - - if (m_multisample_allowed) - // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. - glsafe(::glDisable(GL_MULTISAMPLE)); - - glsafe(::glDisable(GL_BLEND)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - - //BBS: only render plate in view 3D - if (m_canvas_type == ECanvasType::CanvasView3D) { - _render_plates_for_picking(); - } - - m_camera_clipping_plane = m_gizmos.get_clipping_plane(); - if (m_camera_clipping_plane.is_active()) { - ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); - ::glEnable(GL_CLIP_PLANE0); - } - _render_volumes_for_picking(); - if (m_camera_clipping_plane.is_active()) - ::glDisable(GL_CLIP_PLANE0); - - //BBS: remove the bed picking logic - //_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward()); - - m_gizmos.render_current_gizmo_for_picking_pass(); - - if (m_multisample_allowed) - glsafe(::glEnable(GL_MULTISAMPLE)); - - int volume_id = -1; - int gizmo_id = -1; - - GLubyte color[4] = { 0, 0, 0, 0 }; - const Size& cnv_size = get_canvas_size(); - bool inside = 0 <= m_mouse.position(0) && m_mouse.position(0) < cnv_size.get_width() && 0 <= m_mouse.position(1) && m_mouse.position(1) < cnv_size.get_height(); - if (inside) { - glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color)); - if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3]) { - // Only non-interpolated colors are valid, those have their lowest three bits zeroed. - // we reserve color = (0,0,0) for occluders (as the printbed) - // volumes' id are shifted by 1 - // see: _render_volumes_for_picking() - //BBS: remove the bed picking logic - //volume_id = color[0] + (color[1] << 8) + (color[2] << 16) - 1; - volume_id = color[0] + (color[1] << 8) + (color[2] << 16); - // gizmos' id are instead properly encoded by the color - gizmo_id = color[0] + (color[1] << 8) + (color[2] << 16); - } - } - else - m_gizmos.set_hover_id(inside && (unsigned int)gizmo_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - gizmo_id) : -1); - - //BBS: add plate picking logic - int plate_hover_id = PartPlate::PLATE_BASE_ID - volume_id; - if (plate_hover_id >= 0 && plate_hover_id < PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT) { - wxGetApp().plater()->get_partplate_list().set_hover_id(plate_hover_id); - hover_plate_idxs->emplace_back(plate_hover_id); - const_cast(&m_gizmos)->set_hover_id(-1); - } - else { - wxGetApp().plater()->get_partplate_list().reset_hover_id(); - if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) { - // do not add the volume id if any gizmo is active and CTRL is pressed - if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) - hover_volume_idxs->emplace_back(volume_id); - const_cast(&m_gizmos)->set_hover_id(-1); + // Orca: ignore clipping plane if not applying + GLGizmoBase *current_gizmo = m_gizmos.get_current(); + const ClippingPlane clipping_plane = ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() : + ClippingPlane::ClipsNothing()) + .inverted_normal(); + const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane); + if (hit.is_valid()) { + switch (hit.type) + { + case SceneRaycaster::EType::Volume: + { + if (0 <= hit.raycaster_id && hit.raycaster_id < (int)m_volumes.volumes.size()) { + const GLVolume* volume = m_volumes.volumes[hit.raycaster_id]; + if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) { + // do not add the volume id if any gizmo is active and CTRL is pressed + if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) + m_hover_volume_idxs.emplace_back(hit.raycaster_id); + m_gizmos.set_hover_id(-1); + } } else - const_cast(&m_gizmos)->set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); - } + assert(false); - _update_volumes_hover_state(); + break; + } + case SceneRaycaster::EType::Gizmo: + case SceneRaycaster::EType::FallbackGizmo: + { + const Size& cnv_size = get_canvas_size(); + const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && + 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); + m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1); + break; + } + case SceneRaycaster::EType::Bed: + { + // BBS: add plate picking logic + int plate_hover_id = PartPlate::PLATE_BASE_ID - hit.raycaster_id; + if (plate_hover_id >= 0 && plate_hover_id < PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT) { + wxGetApp().plater()->get_partplate_list().set_hover_id(plate_hover_id); + m_hover_plate_idxs.emplace_back(plate_hover_id); + } else { + wxGetApp().plater()->get_partplate_list().reset_hover_id(); + } + m_gizmos.set_hover_id(-1); + break; + } + default: + { + assert(false); + break; + } + } } + else + m_gizmos.set_hover_id(-1); + + _update_volumes_hover_state(); + +#if ENABLE_RAYCAST_PICKING_DEBUG + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize); + std::string object_type = "None"; + switch (hit.type) + { + case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; } + case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; } + case SceneRaycaster::EType::FallbackGizmo: { object_type = "Gizmo2 element"; break; } + case SceneRaycaster::EType::Volume: + { + if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower) + object_type = "Volume (Wipe tower)"; + else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad)) + object_type = "Volume (SLA pad)"; + else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree)) + object_type = "Volume (SLA supports)"; + else if (m_volumes.volumes[hit.raycaster_id]->is_modifier) + object_type = "Volume (Modifier)"; + else + object_type = "Volume (Part)"; + break; + } + default: { break; } + } + + auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color, + const std::string& col_3 = "", const ImVec4& col_3_color = ImGui::GetStyleColorVec4(ImGuiCol_Text)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(col_1_color, col_1.c_str()); + ImGui::TableSetColumnIndex(1); + imgui.text_colored(col_2_color, col_2.c_str()); + if (!col_3.empty()) { + ImGui::TableSetColumnIndex(2); + imgui.text_colored(col_3_color, col_3.c_str()); + } + }; + + char buf[1024]; + if (hit.type != SceneRaycaster::EType::None) { + if (ImGui::BeginTable("Hit", 2)) { + add_strings_row_to_table("Object ID", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(hit.raycaster_id), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Type", ImGuiWrapper::COL_ORANGE_LIGHT, object_type, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); + add_strings_row_to_table("Position", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); + add_strings_row_to_table("Normal", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + } + else + imgui.text("NO HIT"); + + ImGui::Separator(); + imgui.text("Registered for picking:"); + if (ImGui::BeginTable("Raycasters", 2)) { + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); + add_strings_row_to_table("Beds", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); + add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); + add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.fallback_gizmos_count(), (int)m_scene_raycaster.active_fallback_gizmos_count()); + add_strings_row_to_table("Gizmo2 elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + + std::vector>* gizmo_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::Gizmo); + if (gizmo_raycasters != nullptr && !gizmo_raycasters->empty()) { + ImGui::Separator(); + imgui.text("Gizmo raycasters IDs:"); + if (ImGui::BeginTable("GizmoRaycasters", 3)) { + for (size_t i = 0; i < gizmo_raycasters->size(); ++i) { + add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, (*gizmo_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text), + to_string(Geometry::Transformation((*gizmo_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + + std::vector>* gizmo2_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::FallbackGizmo); + if (gizmo2_raycasters != nullptr && !gizmo2_raycasters->empty()) { + ImGui::Separator(); + imgui.text("Gizmo2 raycasters IDs:"); + if (ImGui::BeginTable("Gizmo2Raycasters", 3)) { + for (size_t i = 0; i < gizmo2_raycasters->size(); ++i) { + add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::FallbackGizmo, (*gizmo2_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text), + to_string(Geometry::Transformation((*gizmo2_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + + imgui.end(); +#endif // ENABLE_RAYCAST_PICKING_DEBUG } void GLCanvas3D::_rectangular_selection_picking_pass() @@ -6569,6 +6602,56 @@ void GLCanvas3D::_rectangular_selection_picking_pass() std::set idxs; if (m_picking_enabled) { + const size_t width = std::max(m_rectangle_selection.get_width(), 1); + const size_t height = std::max(m_rectangle_selection.get_height(), 1); + + const OpenGLManager::EFramebufferType framebuffers_type = OpenGLManager::get_framebuffers_type(); + bool use_framebuffer = framebuffers_type != OpenGLManager::EFramebufferType::Unknown; + + GLuint render_fbo = 0; + GLuint render_tex = 0; + GLuint render_depth = 0; + if (use_framebuffer) { + // setup a framebuffer which covers only the selection rectangle + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glGenFramebuffers(1, &render_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)); + } + else { + glsafe(::glGenFramebuffersEXT(1, &render_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo)); + } + glsafe(::glGenTextures(1, &render_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffers(1, &render_depth)); + glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth)); + glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth)); + } + else { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffersEXT(1, &render_depth)); + glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth)); + glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth)); + } + const GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 }; + glsafe(::glDrawBuffers(1, drawBufs)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + use_framebuffer = false; + } + else { + if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) + use_framebuffer = false; + } + } + if (m_multisample_allowed) // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. glsafe(::glDisable(GL_MULTISAMPLE)); @@ -6578,20 +6661,48 @@ void GLCanvas3D::_rectangular_selection_picking_pass() glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - _render_volumes_for_picking(); + Camera& main_camera = wxGetApp().plater()->get_camera(); + Camera framebuffer_camera; + Camera* camera = &main_camera; + if (use_framebuffer) { + // setup a camera which covers only the selection rectangle + const std::array& viewport = camera->get_viewport(); + const double near_left = camera->get_near_left(); + const double near_bottom = camera->get_near_bottom(); + const double near_width = camera->get_near_width(); + const double near_height = camera->get_near_height(); + + const double ratio_x = near_width / double(viewport[2]); + const double ratio_y = near_height / double(viewport[3]); + + const double rect_near_left = near_left + double(m_rectangle_selection.get_left()) * ratio_x; + const double rect_near_bottom = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_bottom())) * ratio_y; + double rect_near_right = near_left + double(m_rectangle_selection.get_right()) * ratio_x; + double rect_near_top = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_top())) * ratio_y; + + if (rect_near_left == rect_near_right) + rect_near_right = rect_near_left + ratio_x; + if (rect_near_bottom == rect_near_top) + rect_near_top = rect_near_bottom + ratio_y; + + framebuffer_camera.look_at(camera->get_position(), camera->get_target(), camera->get_dir_up()); + framebuffer_camera.apply_projection(rect_near_left, rect_near_right, rect_near_bottom, rect_near_top, camera->get_near_z(), camera->get_far_z()); + framebuffer_camera.set_viewport(0, 0, width, height); + framebuffer_camera.apply_viewport(); + camera = &framebuffer_camera; + } + + _render_volumes_for_picking(*camera); //BBS: remove the bed picking logic //_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward()); if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); - int width = std::max((int)m_rectangle_selection.get_width(), 1); - int height = std::max((int)m_rectangle_selection.get_height(), 1); - int px_count = width * height; + const size_t px_count = width * height; - int left = (int)m_rectangle_selection.get_left(); - int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top(); - if (left >= 0 && top >= 0) { + const size_t left = use_framebuffer ? 0 : (size_t)m_rectangle_selection.get_left(); + const size_t top = use_framebuffer ? 0 : (size_t)get_canvas_size().get_height() - (size_t)m_rectangle_selection.get_top(); #define USE_PARALLEL 1 #if USE_PARALLEL struct Pixel @@ -6635,14 +6746,33 @@ void GLCanvas3D::_rectangular_selection_picking_pass() idxs.insert(volume_id); } #endif // USE_PARALLEL - } + if (camera != &main_camera) + main_camera.apply_viewport(); + + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffers(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffers(1, &render_fbo)); + } + else if (framebuffers_type == OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffersEXT(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffersEXT(1, &render_fbo)); + } + + if (render_tex != 0) + glsafe(::glDeleteTextures(1, &render_tex)); } m_hover_volume_idxs.assign(idxs.begin(), idxs.end()); _update_volumes_hover_state(); } -void GLCanvas3D::_render_background() const +void GLCanvas3D::_render_background() { bool use_error_color = false; if (wxGetApp().is_editor()) { @@ -6665,45 +6795,47 @@ void GLCanvas3D::_render_background() const } } - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - glsafe(::glMatrixMode(GL_PROJECTION)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - // Draws a bottom to top gradient over the complete screen. glsafe(::glDisable(GL_DEPTH_TEST)); - ::glBegin(GL_QUADS); + ColorRGBA background_color = m_is_dark ? DEFAULT_BG_LIGHT_COLOR_DARK : DEFAULT_BG_LIGHT_COLOR; + ColorRGBA error_background_color = m_is_dark ? ERROR_BG_LIGHT_COLOR_DARK : ERROR_BG_LIGHT_COLOR; + const ColorRGBA bottom_color = use_error_color ? error_background_color : background_color; - float* background_color = m_is_dark ? DEFAULT_BG_LIGHT_COLOR_DARK : DEFAULT_BG_LIGHT_COLOR; - float* error_background_color = m_is_dark ? ERROR_BG_LIGHT_COLOR_DARK : ERROR_BG_LIGHT_COLOR; + if (!m_background.is_initialized()) { + m_background.reset(); - if (use_error_color) - ::glColor3fv(error_background_color); - else - ::glColor3fv(background_color); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); - ::glVertex2f(-1.0f, -1.0f); - ::glVertex2f(1.0f, -1.0f); + // vertices + init_data.add_vertex(Vec2f(-1.0f, -1.0f), Vec2f(0.0f, 0.0f)); + init_data.add_vertex(Vec2f(1.0f, -1.0f), Vec2f(1.0f, 0.0f)); + init_data.add_vertex(Vec2f(1.0f, 1.0f), Vec2f(1.0f, 1.0f)); + init_data.add_vertex(Vec2f(-1.0f, 1.0f), Vec2f(0.0f, 1.0f)); - if (use_error_color) - ::glColor3fv(error_background_color); - else - ::glColor3fv(background_color); + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); - ::glVertex2f(1.0f, 1.0f); - ::glVertex2f(-1.0f, 1.0f); - glsafe(::glEnd()); + m_background.init_from(std::move(init_data)); + } + + GLShaderProgram* shader = wxGetApp().get_shader("background"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("top_color", bottom_color); + shader->set_uniform("bottom_color", bottom_color); + m_background.render(); + shader->stop_using(); + } glsafe(::glEnable(GL_DEPTH_TEST)); - - glsafe(::glPopMatrix()); - glsafe(::glMatrixMode(GL_MODELVIEW)); - glsafe(::glPopMatrix()); } -void GLCanvas3D::_render_bed(bool bottom, bool show_axes) +void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes) { float scale_factor = 1.0; #if ENABLE_RETINA_GL @@ -6721,27 +6853,12 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) //bool show_texture = true; //BBS set axes mode m_bed.set_axes_mode(m_main_toolbar.is_enabled()); - m_bed.render(*this, bottom, scale_factor, show_axes); + m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes); } -void GLCanvas3D::_render_bed_for_picking(bool bottom) +void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) { - float scale_factor = 1.0; -#if ENABLE_RETINA_GL - scale_factor = m_retina_helper->get_scale_factor(); -#endif // ENABLE_RETINA_GL - - //m_bed.render_for_picking(*this, bottom, scale_factor); -} - -void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) const -{ - wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id, render_cali); -} - -void GLCanvas3D::_render_plates_for_picking() const -{ - wxGetApp().plater()->get_partplate_list().render_for_picking_pass(); + wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali); } void GLCanvas3D::_render_plane() const @@ -6824,15 +6941,16 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with default: case GLVolumeCollection::ERenderType::Opaque: { - const GLGizmosManager& gm = get_gizmos_manager(); + GLGizmosManager& gm = get_gizmos_manager(); if (dynamic_cast(gm.get_current()) == nullptr) { if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; - m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { - // Which volume to paint without the layer height profile shader? - return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); - }); + const Camera& camera = wxGetApp().plater()->get_camera(); + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [object_id](const GLVolume& volume) { + // Which volume to paint without the layer height profile shader? + return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); + }); m_layers_editing.render_volumes(*this, m_volumes); } else { @@ -6844,7 +6962,8 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with }*/ //BBS:add assemble view related logic // do not cull backfaces to show broken geometry, if any - m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) { + const Camera& camera = wxGetApp().plater()->get_camera(); + m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier && !volume.is_wipe_tower; } @@ -6876,8 +6995,9 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with else shader->set_uniform("show_wireframe", false); }*/ + const Camera& camera = wxGetApp().plater()->get_camera(); //BBS:add assemble view related logic - m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier; } @@ -6937,7 +7057,7 @@ void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height) } } -void GLCanvas3D::_render_selection() const +void GLCanvas3D::_render_selection() { float scale_factor = 1.0; #if ENABLE_RETINA_GL @@ -6957,8 +7077,8 @@ void GLCanvas3D::_render_sequential_clearance() { case GLGizmosManager::EType::Flatten: case GLGizmosManager::EType::Cut: - case GLGizmosManager::EType::Hollow: - case GLGizmosManager::EType::SlaSupports: + // case GLGizmosManager::EType::Hollow: + // case GLGizmosManager::EType::SlaSupports: case GLGizmosManager::EType::FdmSupports: case GLGizmosManager::EType::Seam: { return; } default: { break; } @@ -6968,7 +7088,7 @@ void GLCanvas3D::_render_sequential_clearance() } #if ENABLE_RENDER_SELECTION_CENTER -void GLCanvas3D::_render_selection_center() const +void GLCanvas3D::_render_selection_center() { m_selection.render_center(m_gizmos.is_dragging()); } @@ -7032,9 +7152,6 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() float noitems_width = top_tb_width - size * items_cnt; // width of separators and borders in top toolbars // calculate scale needed for items in all top toolbars -#ifdef __WINDOWS__ - cnv_size.set_width(cnv_size.get_width() + m_separator_toolbar.get_width() + collapse_toolbar_width); -#endif float new_h_scale = (cnv_size.get_width() - noitems_width) / (items_cnt * GLToolbar::Default_Icons_Size); //for protect @@ -7071,14 +7188,6 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() void GLCanvas3D::_render_overlays() { glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - // ensure that the textures are renderered inside the frustrum - const Camera& camera = wxGetApp().plater()->get_camera(); - glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.10))); - // ensure that the overlay fits the frustrum near z plane - double gui_scale = camera.get_gui_scale(); - glsafe(::glScaled(gui_scale, gui_scale, 1.0)); _check_and_update_toolbar_icon_scale(); @@ -7150,8 +7259,6 @@ void GLCanvas3D::_render_overlays() }*/ } m_labels.render(sorted_instances); - - glsafe(::glPopMatrix()); } void GLCanvas3D::_render_style_editor() @@ -7247,17 +7354,16 @@ void GLCanvas3D::_render_style_editor() ImGui::End(); } -void GLCanvas3D::_render_volumes_for_picking() const +void GLCanvas3D::_render_volumes_for_picking(const Camera& camera) const { - static const GLfloat INV_255 = 1.0f / 255.0f; + GLShaderProgram* shader = wxGetApp().get_shader("flat_clip"); + if (shader == nullptr) + return; // do not cull backfaces to show broken geometry, if any glsafe(::glDisable(GL_CULL_FACE)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - - const Transform3d& view_matrix = wxGetApp().plater()->get_camera().get_view_matrix(); + const Transform3d& view_matrix = camera.get_view_matrix(); for (size_t type = 0; type < 2; ++ type) { GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::ERenderType::Opaque : GLVolumeCollection::ERenderType::Transparent, view_matrix); for (const GLVolumeWithIdAndZ& volume : to_render) @@ -7266,20 +7372,22 @@ void GLCanvas3D::_render_volumes_for_picking() const // we reserve color = (0,0,0) for occluders (as the printbed) // so we shift volumes' id by 1 to get the proper color //BBS: remove the bed picking logic - unsigned int id = volume.second.first; - //unsigned int id = 1 + volume.second.first; - unsigned int r = (id & (0x000000FF << 0)) << 0; - unsigned int g = (id & (0x000000FF << 8)) >> 8; - unsigned int b = (id & (0x000000FF << 16)) >> 16; - unsigned int a = picking_checksum_alpha_channel(r, g, b); - glsafe(::glColor4f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255)); - volume.first->render(); + const unsigned int id = volume.second.first; + //const unsigned int id = 1 + volume.second.first; + volume.first->model.set_color(picking_decode(id)); + shader->start_using(); + shader->set_uniform("view_model_matrix", view_matrix * volume.first->world_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); + shader->set_uniform("z_range", m_volumes.get_z_range()); + shader->set_uniform("clipping_plane", m_volumes.get_clipping_plane()); + volume.first->picking = true; + volume.first->render(); + volume.first->picking = false; + shader->stop_using(); } } - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnable(GL_CULL_FACE)); } @@ -7319,32 +7427,19 @@ void GLCanvas3D::_render_main_toolbar() if (!m_main_toolbar.is_enabled()) return; - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); + const Size cnv_size = get_canvas_size(); + const float top = 0.5f * (float)cnv_size.get_height(); -#if BBS_TOOLBAR_ON_TOP GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; - float gizmo_width = m_gizmos.get_scaled_total_width(); - float assemble_width = m_assemble_view_toolbar.get_width(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + separator_width + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom; -#else - float gizmo_height = m_gizmos.get_scaled_total_height(); - float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float main_toolbar_height = (float)m_main_toolbar.get_height(); - float assemble_height = m_assemble_view_toolbar.get_height(); - float top = 0.5f * (main_toolbar_height + gizmo_height + assemble_height) * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_main_toolbar.get_width()) * inv_zoom; - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": top %1%, main_toolbar_height %2%, space_height %3% gizmo_height %4%") % top % main_toolbar_height % space_height % gizmo_height; -#endif + const float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; + const float gizmo_width = m_gizmos.get_scaled_total_width(); + const float assemble_width = m_assemble_view_toolbar.get_width(); + const float separator_width = m_separator_toolbar.get_width(); + const float left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + separator_width + gizmo_width + assemble_width - collapse_toolbar_width)); m_main_toolbar.set_position(top, left); m_main_toolbar.render(*this); if (m_toolbar_highlighter.m_render_arrow) - { m_main_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item); - } } //BBS: GUI refactor: GLToolbar adjust @@ -7669,28 +7764,16 @@ void GLCanvas3D::_render_assemble_view_toolbar() const if (!m_assemble_view_toolbar.is_enabled()) return; - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - -#if BBS_TOOLBAR_ON_TOP + const Size cnv_size = get_canvas_size(); GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; - float gizmo_width = m_gizmos.get_scaled_total_width(); - float assemble_width = m_assemble_view_toolbar.get_width(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - separator_width - collapse_toolbar_width)) * inv_zoom; - float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width) * inv_zoom; - //float left = 0.5f * (m_main_toolbar.get_width() + gizmo_width - m_assemble_view_toolbar.get_width() + collapse_toolbar_width) * inv_zoom; -#else - float gizmo_height = m_gizmos.get_scaled_total_height(); - //float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float main_toolbar_height = (float)m_main_toolbar.get_height(); - float assemble_height = (float)m_assemble_view_toolbar.get_height(); - float top = 0.5f * (assemble_height - main_toolbar_height - gizmo_height) * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_assemble_view_toolbar.get_width()) * inv_zoom; - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": top %1%, main_toolbar_height %2%, space_height %3% gizmo_height %4%") % top % main_toolbar_height % space_height % gizmo_height; -#endif + const float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; + const float gizmo_width = m_gizmos.get_scaled_total_width(); + const float assemble_width = m_assemble_view_toolbar.get_width(); + const float separator_width = m_separator_toolbar.get_width(); + const float top = 0.5f * (float)cnv_size.get_height(); + const float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width + separator_width - collapse_toolbar_width)); + const float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width + separator_width); + m_assemble_view_toolbar.set_position(top, left); m_assemble_view_toolbar.render(*this); } @@ -7755,17 +7838,15 @@ void GLCanvas3D::_render_separator_toolbar_right() const if (!m_separator_toolbar.is_enabled()) return; - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - + const Size cnv_size = get_canvas_size(); GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; - float gizmo_width = m_gizmos.get_scaled_total_width(); - float assemble_width = m_assemble_view_toolbar.get_width(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom; - float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width) * inv_zoom; + const float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; + const float gizmo_width = m_gizmos.get_scaled_total_width(); + const float assemble_width = m_assemble_view_toolbar.get_width(); + const float separator_width = m_separator_toolbar.get_width(); + const float top = 0.5f * (float)cnv_size.get_height(); + const float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width + separator_width - collapse_toolbar_width)); + const float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width + separator_width / 2); m_separator_toolbar.set_position(top, left); m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine); @@ -7776,17 +7857,15 @@ void GLCanvas3D::_render_separator_toolbar_left() const if (!m_separator_toolbar.is_enabled()) return; - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - + const Size cnv_size = get_canvas_size(); GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; - float gizmo_width = m_gizmos.get_scaled_total_width(); - float assemble_width = m_assemble_view_toolbar.get_width(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width + separator_width - collapse_toolbar_width)) * inv_zoom; - float left = main_toolbar_left + (m_main_toolbar.get_width()) * inv_zoom; + const float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; + const float gizmo_width = m_gizmos.get_scaled_total_width(); + const float assemble_width = m_assemble_view_toolbar.get_width(); + const float separator_width = m_separator_toolbar.get_width(); + const float top = 0.5f * (float)cnv_size.get_height(); + const float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width + separator_width - collapse_toolbar_width)); + const float left = main_toolbar_left + (m_main_toolbar.get_width()); m_separator_toolbar.set_position(top, left); m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine); @@ -7796,12 +7875,10 @@ void GLCanvas3D::_render_collapse_toolbar() const { GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - //float left = (0.5f * (float)cnv_size.get_width() - (float)collapse_toolbar.get_width() - band) * inv_zoom; - float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; + const Size cnv_size = get_canvas_size(); + const float top = 0.5f * (float)cnv_size.get_height(); + //const float left = (0.5f * (float)cnv_size.get_width() - (float)collapse_toolbar.get_width() - band); + const float left = -0.5f * (float)cnv_size.get_width(); collapse_toolbar.set_position(top, left); collapse_toolbar.render(*this); @@ -7870,15 +7947,15 @@ void GLCanvas3D::_render_paint_toolbar() const ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImGuiContext& context = *GImGui; bool disabled = !wxGetApp().plater()->can_fillcolor(); - unsigned char rgb[3]; + ColorRGBA rgba; for (int i = 0; i < extruder_num; i++) { if (i > 0) ImGui::SameLine(); - Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb); - ImGui::PushStyleColor(ImGuiCol_Button, ImColor(rgb[0], rgb[1], rgb[2]).Value); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor(rgb[0], rgb[1], rgb[2]).Value); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor(rgb[0], rgb[1], rgb[2]).Value); + decode_color(colors[i], rgba); + ImGui::PushStyleColor(ImGuiCol_Button, ImGuiWrapper::to_ImVec4(rgba)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiWrapper::to_ImVec4(rgba)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGuiWrapper::to_ImVec4(rgba)); if (disabled) ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); if (ImGui::Button(("##filament_button" + std::to_string(i)).c_str(), button_size)) { @@ -7899,9 +7976,9 @@ void GLCanvas3D::_render_paint_toolbar() const } const float text_offset_y = 4.0f * em_unit * f_scale; - for (int i = 0; i < extruder_num; i++){ - Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb); - float gray = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]; + for (int i = 0; i < extruder_num; i++) { + decode_color(colors[i], rgba); + float gray = 0.299 * rgba.r_uchar() + 0.587 * rgba.g_uchar() + 0.114 * rgba.b_uchar(); ImVec4 text_color = gray < 80 ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0, 0, 0, 1.0f); imgui.push_bold_font(); @@ -8078,28 +8155,58 @@ void GLCanvas3D::_render_assemble_info() const } #if ENABLE_SHOW_CAMERA_TARGET -void GLCanvas3D::_render_camera_target() const +void GLCanvas3D::_render_camera_target() { - double half_length = 5.0; + static const float half_length = 5.0f; glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glLineWidth(2.0f)); - ::glBegin(GL_LINES); - const Vec3d& target = wxGetApp().plater()->get_camera().get_target(); - // draw line for x axis - ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3d(target(0) - half_length, target(1), target(2)); - ::glVertex3d(target(0) + half_length, target(1), target(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3d(target(0), target(1) - half_length, target(2)); - ::glVertex3d(target(0), target(1) + half_length, target(2)); - // draw line for z axis - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3d(target(0), target(1), target(2) - half_length); - ::glVertex3d(target(0), target(1), target(2) + half_length); - glsafe(::glEnd()); + const Vec3f& target = wxGetApp().plater()->get_camera().get_target().cast(); + bool target_changed = !m_camera_target.target.isApprox(target.cast()); + m_camera_target.target = target.cast(); + + for (int i = 0; i < 3; ++i) { + if (!m_camera_target.axis[i].is_initialized() || target_changed) { + m_camera_target.axis[i].reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; + init_data.color = (i == X) ? ColorRGBA::X() : ((i == Y) ? ColorRGBA::Y() : ColorRGBA::Z()); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + if (i == X) { + init_data.add_vertex(Vec3f(target.x() - half_length, target.y(), target.z())); + init_data.add_vertex(Vec3f(target.x() + half_length, target.y(), target.z())); + } + else if (i == Y) { + init_data.add_vertex(Vec3f(target.x(), target.y() - half_length, target.z())); + init_data.add_vertex(Vec3f(target.x(), target.y() + half_length, target.z())); + } + else { + init_data.add_vertex(Vec3f(target.x(), target.y(), target.z() - half_length)); + init_data.add_vertex(Vec3f(target.x(), target.y(), target.z() + half_length)); + } + + // indices + init_data.add_line(0, 1); + + m_camera_target.axis[i].init_from(std::move(init_data)); + } + } + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + for (int i = 0; i < 3; ++i) { + m_camera_target.axis[i].render(); + } + shader->stop_using(); + } } #endif // ENABLE_SHOW_CAMERA_TARGET @@ -8122,32 +8229,51 @@ void GLCanvas3D::_render_sla_slices() if (!obj->is_step_done(slaposSliceSupports)) continue; - SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); - SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); + SlaCap::ObjectIdToModelsMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); + SlaCap::ObjectIdToModelsMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); { if (it_caps_bottom == m_sla_caps[0].triangles.end()) it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; if (!m_sla_caps[0].matches(clip_min_z)) { m_sla_caps[0].z = clip_min_z; - it_caps_bottom->second.object.clear(); - it_caps_bottom->second.supports.clear(); + it_caps_bottom->second.object.reset(); + it_caps_bottom->second.supports.reset(); } if (it_caps_top == m_sla_caps[1].triangles.end()) it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; if (!m_sla_caps[1].matches(clip_max_z)) { m_sla_caps[1].z = clip_max_z; - it_caps_top->second.object.clear(); - it_caps_top->second.supports.clear(); + it_caps_top->second.object.reset(); + it_caps_top->second.supports.reset(); } } - Pointf3s &bottom_obj_triangles = it_caps_bottom->second.object; - Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports; - Pointf3s &top_obj_triangles = it_caps_top->second.object; - Pointf3s &top_sup_triangles = it_caps_top->second.supports; + GLModel& bottom_obj_triangles = it_caps_bottom->second.object; + GLModel& bottom_sup_triangles = it_caps_bottom->second.supports; + GLModel& top_obj_triangles = it_caps_top->second.object; + GLModel& top_sup_triangles = it_caps_top->second.supports; - if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && - !obj->get_slice_index().empty()) - { + auto init_model = [](GLModel& model, const Pointf3s& triangles, const ColorRGBA& color) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(triangles.size()); + init_data.reserve_indices(triangles.size() / 3); + init_data.color = color; + + unsigned int vertices_count = 0; + for (const Vec3d& v : triangles) { + init_data.add_vertex((Vec3f)v.cast()); + ++vertices_count; + if (vertices_count % 3 == 0) { + init_data.add_triangle(vertices_count - 3, vertices_count - 2, vertices_count - 1); + } + } + + if (!init_data.is_empty()) + model.init_from(std::move(init_data)); + }; + + if ((!bottom_obj_triangles.is_initialized() || !bottom_sup_triangles.is_initialized() || + !top_obj_triangles.is_initialized() || !top_sup_triangles.is_initialized()) && !obj->get_slice_index().empty()) { double layer_height = print->default_object_config().layer_height.value; double initial_layer_height = print->material_config().initial_layer_height.value; bool left_handed = obj->is_left_handed(); @@ -8168,60 +8294,54 @@ void GLCanvas3D::_render_sla_slices() const ExPolygons& obj_bottom = slice_low.get_slice(soModel); const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); // calculate model bottom cap - if (bottom_obj_triangles.empty() && !obj_bottom.empty()) - bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed); + // calculate model bottom cap + if (!bottom_obj_triangles.is_initialized() && !obj_bottom.empty()) + init_model(bottom_obj_triangles, triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.37f, 0.0f, 1.0f }); // calculate support bottom cap - if (bottom_sup_triangles.empty() && !sup_bottom.empty()) - bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, ! left_handed); + if (!bottom_sup_triangles.is_initialized() && !sup_bottom.empty()) + init_model(bottom_sup_triangles, triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.0f, 0.37f, 1.0f }); } if (slice_high.is_valid()) { const ExPolygons& obj_top = slice_high.get_slice(soModel); const ExPolygons& sup_top = slice_high.get_slice(soSupport); // calculate model top cap - if (top_obj_triangles.empty() && !obj_top.empty()) - top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed); + // calculate model top cap + if (!top_obj_triangles.is_initialized() && !obj_top.empty()) + init_model(top_obj_triangles, triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.37f, 0.0f, 1.0f }); // calculate support top cap - if (top_sup_triangles.empty() && !sup_top.empty()) - top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed); + if (!top_sup_triangles.is_initialized() && !sup_top.empty()) + init_model(top_sup_triangles, triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.0f, 0.37f, 1.0f }); } } - if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) { - for (const SLAPrintObject::Instance& inst : obj->instances()) { - glsafe(::glPushMatrix()); - glsafe(::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0)); - glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0)); - if (obj->is_left_handed()) - // The polygons are mirrored by X. - glsafe(::glScalef(-1.0, 1.0, 1.0)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); - if (!bottom_obj_triangles.empty()) { - glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_obj_triangles.front().data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, bottom_obj_triangles.size())); - } - if (! top_obj_triangles.empty()) { - glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_obj_triangles.front().data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, top_obj_triangles.size())); - } - glsafe(::glColor3f(1.0f, 0.0f, 0.37f)); - if (! bottom_sup_triangles.empty()) { - glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_sup_triangles.front().data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, bottom_sup_triangles.size())); - } - if (! top_sup_triangles.empty()) { - glsafe(::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_sup_triangles.front().data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, top_sup_triangles.size())); - } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glPopMatrix()); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + for (const SLAPrintObject::Instance& inst : obj->instances()) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(Vec3d(unscale(inst.shift.x()), unscale(inst.shift.y()), 0.0), + inst.rotation * Vec3d::UnitZ(), Vec3d::Ones(), + obj->is_left_handed() ? Vec3d(-1.0f, 1.0f, 1.0f) : Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + bottom_obj_triangles.render(); + top_obj_triangles.render(); + bottom_sup_triangles.render(); + top_sup_triangles.render(); + } + + shader->stop_using(); } } } -void GLCanvas3D::_render_selection_sidebar_hints() const +void GLCanvas3D::_render_selection_sidebar_hints() { m_selection.render_sidebar_hints(m_sidebar_field, m_gizmos.get_uniform_scaling()); } @@ -8331,21 +8451,17 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) if (m_canvas == nullptr) return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); - const Camera& camera = wxGetApp().plater()->get_camera(); - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection= camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); - - GLint y = viewport[3] - (GLint)mouse_pos(1); - GLfloat mouse_z; - if (z == nullptr) - glsafe(::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z)); - else - mouse_z = *z; - - Vec3d out; - igl::unproject(Vec3d(mouse_pos(0), y, mouse_z), modelview, projection, viewport, out); - return out; + if (z == nullptr) { + const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(mouse_pos.cast(), wxGetApp().plater()->get_camera(), nullptr); + return hit.is_valid() ? hit.position.cast() : _mouse_to_bed_3d(mouse_pos); + } + else { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Vec4i viewport(camera.get_viewport().data()); + Vec3d out; + igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), *z), camera.get_view_matrix().matrix(), camera.get_projection_matrix().matrix(), viewport, out); + return out; + } } Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) @@ -8375,7 +8491,7 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume) if (!print->has_skirt() && !print->has_brim()) return; - const std::array color = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish + const ColorRGBA color = ColorRGBA::GREENISH(); // number of skirt layers size_t total_layer_count = 0; @@ -8401,27 +8517,29 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume) skirt_height = std::min(skirt_height, print_zs.size()); print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); - GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE); + GLVolume* volume = m_volumes.new_toolpath_volume(color); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; for (size_t i = 0; i < skirt_height; ++ i) { volume->print_zs.emplace_back(print_zs[i]); - volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size()); + volume->offsets.emplace_back(init_data.indices_count()); //BBS: usage of m_brim are deleted - _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume); + _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), init_data); // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. - if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - GLVolume &vol = *volume; + if (init_data.vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + volume->model.init_from(std::move(init_data)); + GLVolume &vol = *volume; volume = m_volumes.new_toolpath_volume(vol.color); - reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); } } - volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box()); - volume->indexed_vertex_array.finalize_geometry(m_initialized); + volume->model.init_from(std::move(init_data)); + volume->is_outside = !contains(build_volume, volume->model); } void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector& str_tool_colors, const std::vector& color_print_values) { - std::vector> tool_colors = _parse_colors(str_tool_colors); + std::vector tool_colors; + decode_colors(str_tool_colors, tool_colors); struct Ctxt { @@ -8430,20 +8548,20 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c bool has_perimeters; bool has_infill; bool has_support; - const std::vector>* tool_colors; + const std::vector* tool_colors; bool is_single_material_print; int filaments_cnt; const std::vector* color_print_values; - static const std::array& color_perimeters() { static std::array color = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow - static const std::array& color_infill() { static std::array color = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish - static const std::array& color_support() { static std::array color = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - static const std::array& color_pause_or_custom_code() { static std::array color = { 0.5f, 0.5f, 0.5f, 1.f }; return color; } // gray + static ColorRGBA color_perimeters() { return ColorRGBA::YELLOW(); } + static ColorRGBA color_infill() { return ColorRGBA::REDISH(); } + static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } + static ColorRGBA color_pause_or_custom_code() { return ColorRGBA::GRAY(); } // For cloring by a tool, return a parsed color. bool color_by_tool() const { return tool_colors != nullptr; } size_t number_tools() const { return color_by_tool() ? tool_colors->size() : 0; } - const std::array& color_tool(size_t tool) const { return (*tool_colors)[tool]; } + const ColorRGBA& color_tool(size_t tool) const { return (*tool_colors)[tool]; } // For coloring by a color_print(M600), return a parsed color. bool color_by_color_print() const { return color_print_values!=nullptr; } @@ -8583,11 +8701,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); tbb::spin_mutex new_volume_mutex; - auto new_volume = [this, &new_volume_mutex](const std::array& color) { + auto new_volume = [this, &new_volume_mutex](const ColorRGBA& color) { // Allocate the volume before locking. GLVolume *volume = new GLVolume(color); volume->is_extrusion_path = true; - tbb::spin_mutex::scoped_lock lock; + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); + tbb::spin_mutex::scoped_lock lock; // Lock by ROII, so if the emplace_back() fails, the lock will be released. lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -8599,31 +8720,36 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range& range) { GLVolumePtrs vols; - auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { - return *vols[ctxt.color_by_color_print()? + std::vector geometries; + auto select_geometry = [&ctxt, &geometries](size_t layer_idx, int extruder, int feature) -> GLModel::Geometry& { + return geometries[ctxt.color_by_color_print() ? ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : - ctxt.color_by_tool() ? - std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : - feature - ]; + ctxt.color_by_tool() ? + std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) : + feature + ]; }; if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); + geometries.emplace_back(GLModel::Geometry()); + } } - else + else { vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; - for (GLVolume *vol : vols) - // Reserving number of vertices (3x position + 3x color) - vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); + geometries = { GLModel::Geometry(), GLModel::Geometry(), GLModel::Geometry() }; + } + + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + } for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; - if (is_selected_separate_extruder) - { + if (is_selected_separate_extruder) { bool at_least_one_has_correct_extruder = false; - for (const LayerRegion* layerm : layer->regions()) - { + for (const LayerRegion* layerm : layer->regions()) { if (layerm->slices.surfaces.empty()) continue; const PrintRegionConfig& cfg = layerm->region().config(); @@ -8638,12 +8764,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c continue; } - for (GLVolume *vol : vols) + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume* vol = vols[i]; if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { vol->print_zs.emplace_back(layer->print_z); - vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size()); - vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size()); + vol->offsets.emplace_back(geometries[i].indices_count()); } + } + for (const PrintInstance &instance : *ctxt.shifted_copies) { const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { @@ -8657,18 +8785,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } if (ctxt.has_perimeters) _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - volume(idx_layer, layerm->region().config().wall_filament.value, 0)); + select_geometry(idx_layer, layerm->region().config().wall_filament.value, 0)); if (ctxt.has_infill) { for (const ExtrusionEntity *ee : layerm->fills.entities) { // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); if (! fill->entities.empty()) _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, - volume(idx_layer, - is_solid_infill(fill->entities.front()->role()) ? - layerm->region().config().solid_infill_filament : - layerm->region().config().sparse_infill_filament, - 1)); + select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ? + layerm->region().config().solid_infill_filament : + layerm->region().config().sparse_infill_filament, 1)); } } } @@ -8677,40 +8803,42 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c if (support_layer) { for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, - volume(idx_layer, - (extrusion_entity->role() == erSupportMaterial || - extrusion_entity->role() == erSupportTransition) ? - support_layer->object()->config().support_filament : - support_layer->object()->config().support_interface_filament, - 2)); + select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial || extrusion_entity->role() == erSupportTransition) ? + support_layer->object()->config().support_filament : + support_layer->object()->config().support_interface_filament, 2)); } } } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - vols[i] = new_volume(vol.color); - reserve_new_volume_finalize_old_volume(*vols[i], vol, false); - } + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); + vols[i] = new_volume(vol.color); + } } } - for (GLVolume *vol : vols) - // Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver, - // but this code runs in parallel and the OpenGL driver is not thread safe. - vol->indexed_vertex_array.shrink_to_fit(); + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } }); BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. - m_volumes.volumes.erase( - std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - m_volumes.volumes.end()); + { + for (auto ptr_it = m_volumes.volumes.begin() + volumes_cnt_initial; ptr_it != m_volumes.volumes.end(); ++ptr_it) + if ((*ptr_it)->empty()) { + delete *ptr_it; + *ptr_it = nullptr; + } + m_volumes.volumes.erase(std::remove(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), nullptr), m_volumes.volumes.end()); + } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; - v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); - v->indexed_vertex_array.finalize_geometry(m_initialized); + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); } BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -8725,21 +8853,22 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con if (!print->is_step_done(psWipeTower)) return; - std::vector> tool_colors = _parse_colors(str_tool_colors); + std::vector tool_colors; + decode_colors(str_tool_colors, tool_colors); struct Ctxt { - const Print *print; - const std::vector>* tool_colors; - Vec2f wipe_tower_pos; - float wipe_tower_angle; + const Print *print; + const std::vector *tool_colors; + Vec2f wipe_tower_pos; + float wipe_tower_angle; - static const std::array& color_support() { static std::array color = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } // For cloring by a tool, return a parsed color. bool color_by_tool() const { return tool_colors != nullptr; } size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() : 0; } - const std::array& color_tool(size_t tool) const { return (*tool_colors)[tool]; } + const ColorRGBA& color_tool(size_t tool) const { return (*tool_colors)[tool]; } int volume_idx(int tool, int feature) const { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(tool, 0)) : feature; } @@ -8779,9 +8908,12 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con size_t n_items = print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); size_t grain_size = std::max(n_items / 128, size_t(1)); tbb::spin_mutex new_volume_mutex; - auto new_volume = [this, &new_volume_mutex](const std::array& color) { + auto new_volume = [this, &new_volume_mutex](const ColorRGBA& color) { auto *volume = new GLVolume(color); volume->is_extrusion_path = true; + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); tbb::spin_mutex::scoped_lock lock; lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -8795,23 +8927,29 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con [&ctxt, &new_volume](const tbb::blocked_range& range) { // Bounding box of this slab of a wipe tower. GLVolumePtrs vols; + std::vector geometries; if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); + geometries.emplace_back(GLModel::Geometry()); + } } - else + else { vols = { new_volume(ctxt.color_support()) }; - for (GLVolume *volume : vols) - // Reserving number of vertices (3x position + 3x color) - volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); + geometries = { GLModel::Geometry() }; + } + + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + } for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { const std::vector &layer = ctxt.tool_change(idx_layer); for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { vol.print_zs.emplace_back(layer.front().print_z); - vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size()); + vol.offsets.emplace_back(geometries[i].indices_count()); } } for (const WipeTower::ToolChangeResult &extrusions : layer) { @@ -8854,31 +8992,38 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con e_prev = e; } _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, - *vols[ctxt.volume_idx(e.tool, 0)]); + geometries[ctxt.volume_idx(e.tool, 0)]); } } } for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); vols[i] = new_volume(vol.color); - reserve_new_volume_finalize_old_volume(*vols[i], vol, false); } } - for (GLVolume *vol : vols) - vol->indexed_vertex_array.shrink_to_fit(); - }); + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } + }); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. - m_volumes.volumes.erase( - std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - m_volumes.volumes.end()); + { + for (auto ptr_it = m_volumes.volumes.begin() + volumes_cnt_initial; ptr_it != m_volumes.volumes.end(); ++ptr_it) + if ((*ptr_it)->empty()) { + delete *ptr_it; + *ptr_it = nullptr; + } + m_volumes.volumes.erase(std::remove(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), nullptr), m_volumes.volumes.end()); + } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; - v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); - v->indexed_vertex_array.finalize_geometry(m_initialized); + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); } BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -8898,15 +9043,14 @@ void GLCanvas3D::_load_sla_shells() return; auto add_volume = [this](const SLAPrintObject &object, int volume_id, const SLAPrintObject::Instance& instance, - const TriangleMesh& mesh, const std::array& color, bool outside_printer_detection_enabled) { + const TriangleMesh& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) { m_volumes.volumes.emplace_back(new GLVolume(color)); GLVolume& v = *m_volumes.volumes.back(); #if ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.load_mesh(mesh, true); + v.model.init_from(mesh, true); #else - v.indexed_vertex_array.load_mesh(mesh); + v.model.init_from(mesh); #endif // ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.finalize_geometry(m_initialized); v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); @@ -8970,28 +9114,6 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) _set_warning_notification(warning, show); } -std::vector> GLCanvas3D::_parse_colors(const std::vector& colors) -{ - static const float INV_255 = 1.0f / 255.0f; - - std::vector> output(colors.size(), { 1.0f, 1.0f, 1.0f, 1.0f }); - for (size_t i = 0; i < colors.size(); ++i) { - const std::string& color = colors[i]; - const char* c = color.data() + 1; - if (color.size() == 7 && color.front() == '#') { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) - break; - - output[i][j] = float(digit1 * 16 + digit2) * INV_255; - } - } - } - return output; -} - void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) { enum ErrorType{ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fb1780c7bc..523b0d5f6e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1,3 +1,8 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral +///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_ @@ -16,6 +21,7 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GCodeViewer.hpp" #include "Camera.hpp" +#include "SceneRaycaster.hpp" #include "IMToolbar.hpp" #include "libslic3r/Slicing.hpp" @@ -62,25 +68,24 @@ class RetinaHelper; class Size { - int m_width; - int m_height; - float m_scale_factor; + int m_width{ 0 }; + int m_height{ 0 }; + float m_scale_factor{ 1.0f }; public: - Size(); - Size(int width, int height, float scale_factor = 1.0); + Size() = default; + Size(int width, int height, float scale_factor = 1.0f) : m_width(width), m_height(height), m_scale_factor(scale_factor) {} - int get_width() const; - void set_width(int width); + int get_width() const { return m_width; } + void set_width(int width) { m_width = width; } - int get_height() const; - void set_height(int height); + int get_height() const { return m_height; } + void set_height(int height) { m_height = height; } - int get_scale_factor() const; - void set_scale_factor(int height); + float get_scale_factor() const { return m_scale_factor; } + void set_scale_factor(float factor) { m_scale_factor = factor; } }; - class RenderTimerEvent : public wxEvent { public: @@ -199,14 +204,6 @@ class GLCanvas3D static const double DefaultCameraZoomToBedMarginFactor; static const double DefaultCameraZoomToPlateMarginFactor; - - static float DEFAULT_BG_LIGHT_COLOR[3]; - static float ERROR_BG_LIGHT_COLOR[3]; - static float DEFAULT_BG_LIGHT_COLOR_LIGHT[3]; - static float ERROR_BG_LIGHT_COLOR_LIGHT[3]; - static float DEFAULT_BG_LIGHT_COLOR_DARK[3]; - static float ERROR_BG_LIGHT_COLOR_DARK[3]; - static void update_render_colors(); static void load_render_colors(); @@ -265,6 +262,15 @@ class GLCanvas3D int last_object_id{ -1 }; float last_z{ 0.0f }; LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE }; + struct Profile + { + GLModel baseline; + GLModel profile; + GLModel background; + float old_canvas_width{ 0.0f }; + std::vector old_layer_height_profile; + }; + Profile m_profile; LayersEditing() = default; ~LayersEditing(); @@ -293,7 +299,6 @@ class GLCanvas3D static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static Rect get_bar_rect_screen(const GLCanvas3D& canvas); - static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; } float object_max_z() const { return m_object_max_z; } @@ -303,10 +308,8 @@ class GLCanvas3D private: bool is_initialized() const; void generate_layer_height_texture(); - - void render_background_texture(const GLCanvas3D& canvas, const Rect& bar_rect); - void render_curve(const Rect& bar_rect); - + void render_active_object_annotations(const GLCanvas3D& canvas); + void render_profile(const GLCanvas3D& canvas); void update_slicing_parameters(); static float thickness_bar_width(const GLCanvas3D& canvas); @@ -356,12 +359,12 @@ class GLCanvas3D { struct Triangles { - Pointf3s object; - Pointf3s supports; + GLModel object; + GLModel supports; }; - typedef std::map ObjectIdToTrianglesMap; + typedef std::map ObjectIdToModelsMap; double z; - ObjectIdToTrianglesMap triangles; + ObjectIdToModelsMap triangles; SlaCap() { reset(); } void reset() { z = DBL_MAX; triangles.clear(); } @@ -504,6 +507,7 @@ private: bool m_is_dark = false; wxGLCanvas* m_canvas; wxGLContext* m_context; + SceneRaycaster m_scene_raycaster; Bed3D &m_bed; #if ENABLE_RETINA_GL std::unique_ptr m_retina_helper; @@ -527,7 +531,7 @@ private: std::array m_clipping_planes; ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; - SlaCap m_sla_caps[2]; + std::array m_sla_caps; std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() @@ -578,14 +582,8 @@ private: // I just don't want to do it now before a release (Lukas Matena 24.3.2019) bool m_render_sla_auxiliaries; - std::string m_color_by; - bool m_reload_delayed; -#if ENABLE_RENDER_PICKING_PASS - bool m_show_picking_texture; -#endif // ENABLE_RENDER_PICKING_PASS - RenderStats m_render_stats; int m_imgui_undo_redo_hovered_pos{ -1 }; @@ -706,6 +704,16 @@ public: } m_gizmo_highlighter; +#if ENABLE_SHOW_CAMERA_TARGET + struct CameraTarget + { + std::array axis; + Vec3d target{ Vec3d::Zero() }; + }; + + CameraTarget m_camera_target; +#endif // ENABLE_SHOW_CAMERA_TARGET + GLModel m_background; public: explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed); ~GLCanvas3D(); @@ -722,6 +730,25 @@ public: bool init(); void post_event(wxEvent &&event); + std::shared_ptr add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, + const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) { + return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces); + } + void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) { + m_scene_raycaster.remove_raycasters(type, id); + } + void remove_raycasters_for_picking(SceneRaycaster::EType type) { + m_scene_raycaster.remove_raycasters(type); + } + + std::vector>* get_raycasters_for_picking(SceneRaycaster::EType type) { + return m_scene_raycaster.get_raycasters(type); + } + + void set_raycaster_gizmos_on_top(bool value) { + m_scene_raycaster.set_gizmos_on_top(value); + } + void reset_explosion_ratio() { m_explosion_ratio = 1.0; } void on_change_color_mode(bool is_dark, bool reinit = true); const bool get_dark_mode_status() { return m_is_dark; } @@ -780,7 +807,9 @@ public: bool get_use_clipping_planes() const { return m_use_clipping_planes; } const std::array &get_clipping_planes() const { return m_clipping_planes; }; - void set_color_by(const std::string& value); + void set_use_color_clip_plane(bool use) { m_volumes.set_use_color_clip_plane(use); } + void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); } + void set_color_clip_plane_colors(const std::array& colors) { m_volumes.set_color_clip_plane_colors(colors); } void refresh_camera_scene_box(); @@ -857,15 +886,15 @@ public: void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, const GLVolumeCollection& volumes, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); static void render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, - const GLVolumeCollection& volumes, std::vector>& extruder_colors, + const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); // render thumbnail using an off-screen framebuffer static void render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, - PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); // render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported static void render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, - PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); //BBS use gcoder viewer render calibration thumbnails @@ -937,7 +966,6 @@ public: void do_move(const std::string& snapshot_type); void do_rotate(const std::string& snapshot_type); void do_scale(const std::string& snapshot_type); - void do_flatten(const Vec3d& normal, const std::string& snapshot_type); void do_center(); void do_mirror(const std::string& snapshot_type); @@ -1111,27 +1139,25 @@ private: void _picking_pass(); void _rectangular_selection_picking_pass(); - void _render_background() const; - void _render_bed(bool bottom, bool show_axes); - void _render_bed_for_picking(bool bottom); + void _render_background(); + void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes); //BBS: add part plate related logic - void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false) const; - void _render_plates_for_picking() const; + void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false); //BBS: add outline drawing logic void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); //BBS: GUI refactor: add canvas size as parameters void _render_gcode(int canvas_width, int canvas_height); //BBS: render a plane for assemble void _render_plane() const; - void _render_selection() const; + void _render_selection(); void _render_sequential_clearance(); #if ENABLE_RENDER_SELECTION_CENTER - void _render_selection_center() const; + void _render_selection_center(); #endif // ENABLE_RENDER_SELECTION_CENTER void _check_and_update_toolbar_icon_scale(); void _render_overlays(); void _render_style_editor(); - void _render_volumes_for_picking() const; + void _render_volumes_for_picking(const Camera& camera) const; void _render_current_gizmo() const; void _render_gizmos_overlay(); void _render_main_toolbar(); @@ -1147,15 +1173,15 @@ private: void _render_assemble_control() const; void _render_assemble_info() const; #if ENABLE_SHOW_CAMERA_TARGET - void _render_camera_target() const; + void _render_camera_target(); #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices(); - void _render_selection_sidebar_hints() const; + void _render_selection_sidebar_hints(); //BBS: GUI refactor: adjust main toolbar position bool _render_orient_menu(float left, float right, float bottom, float top); bool _render_arrange_menu(float left, float right, float bottom, float top); // render thumbnail using the default framebuffer - void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type); + void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type); void _update_volumes_hover_state(); @@ -1201,8 +1227,6 @@ private: // BBS FIXME float get_overlay_window_width() { return 0; /*LayersEditing::get_overlay_window_width();*/ } - - static std::vector> _parse_colors(const std::vector& colors); }; } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 422b654080..959c9aa9ac 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "GLModel.hpp" @@ -8,135 +12,513 @@ #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" +#include "libslic3r/BuildVolume.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" #include #include +#if ENABLE_SMOOTH_NORMALS +#include +#include +#include +#endif // ENABLE_SMOOTH_NORMALS + #include namespace Slic3r { namespace GUI { -size_t GLModel::InitializationData::vertices_count() const +#if ENABLE_SMOOTH_NORMALS +static void smooth_normals_corner(const TriangleMesh& mesh, std::vector& normals) { - size_t ret = 0; - for (const Entity& entity : entities) { - ret += entity.positions.size(); + using MapMatrixXfUnaligned = Eigen::Map>; + using MapMatrixXiUnaligned = Eigen::Map>; + + std::vector face_normals = its_face_normals(mesh.its); + + Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), 3).cast(); + Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), 3); + Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), + Eigen::Index(face_normals.size()), 3).cast(); + Eigen::MatrixXd out_normals; + + igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); + + normals = std::vector(mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast(); + } } - return ret; +} +#endif // ENABLE_SMOOTH_NORMALS + +void GLModel::Geometry::add_vertex(const Vec2f& position) +{ + assert(format.vertex_layout == EVertexLayout::P2); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); } -size_t GLModel::InitializationData::indices_count() const +void GLModel::Geometry::add_vertex(const Vec2f& position, const Vec2f& tex_coord) { - size_t ret = 0; - for (const Entity& entity : entities) { - ret += entity.indices.size(); - } - return ret; + assert(format.vertex_layout == EVertexLayout::P2T2); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(tex_coord.x()); + vertices.emplace_back(tex_coord.y()); } -void GLModel::init_from(const InitializationData& data) +void GLModel::Geometry::add_vertex(const Vec3f& position) { - if (!m_render_data.empty()) // call reset() if you want to reuse this model + assert(format.vertex_layout == EVertexLayout::P3); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(position.z()); +} + +void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec2f& tex_coord) +{ + assert(format.vertex_layout == EVertexLayout::P3T2); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(position.z()); + vertices.emplace_back(tex_coord.x()); + vertices.emplace_back(tex_coord.y()); +} + +void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal) +{ + assert(format.vertex_layout == EVertexLayout::P3N3); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(position.z()); + vertices.emplace_back(normal.x()); + vertices.emplace_back(normal.y()); + vertices.emplace_back(normal.z()); +} + +void GLModel::Geometry::add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord) +{ + assert(format.vertex_layout == EVertexLayout::P3N3T2); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(position.z()); + vertices.emplace_back(normal.x()); + vertices.emplace_back(normal.y()); + vertices.emplace_back(normal.z()); + vertices.emplace_back(tex_coord.x()); + vertices.emplace_back(tex_coord.y()); +} + +void GLModel::Geometry::add_vertex(const Vec4f& position) +{ + assert(format.vertex_layout == EVertexLayout::P4); + vertices.emplace_back(position.x()); + vertices.emplace_back(position.y()); + vertices.emplace_back(position.z()); + vertices.emplace_back(position.w()); +} + +void GLModel::Geometry::add_index(unsigned int id) +{ + indices.emplace_back(id); +} + +void GLModel::Geometry::add_line(unsigned int id1, unsigned int id2) +{ + indices.emplace_back(id1); + indices.emplace_back(id2); +} + +void GLModel::Geometry::add_triangle(unsigned int id1, unsigned int id2, unsigned int id3) +{ + indices.emplace_back(id1); + indices.emplace_back(id2); + indices.emplace_back(id3); +} + +Vec2f GLModel::Geometry::extract_position_2(size_t id) const +{ + const size_t p_stride = position_stride_floats(format); + if (p_stride != 2) { + assert(false); + return { FLT_MAX, FLT_MAX }; + } + + if (vertices_count() <= id) { + assert(false); + return { FLT_MAX, FLT_MAX }; + } + + const float* start = &vertices[id * vertex_stride_floats(format) + position_offset_floats(format)]; + return { *(start + 0), *(start + 1) }; +} + +Vec3f GLModel::Geometry::extract_position_3(size_t id) const +{ + const size_t p_stride = position_stride_floats(format); + if (p_stride != 3) { + assert(false); + return { FLT_MAX, FLT_MAX, FLT_MAX }; + } + + if (vertices_count() <= id) { + assert(false); + return { FLT_MAX, FLT_MAX, FLT_MAX }; + } + + const float* start = &vertices[id * vertex_stride_floats(format) + position_offset_floats(format)]; + return { *(start + 0), *(start + 1), *(start + 2) }; +} + +Vec3f GLModel::Geometry::extract_normal_3(size_t id) const +{ + const size_t n_stride = normal_stride_floats(format); + if (n_stride != 3) { + assert(false); + return { FLT_MAX, FLT_MAX, FLT_MAX }; + } + + if (vertices_count() <= id) { + assert(false); + return { FLT_MAX, FLT_MAX, FLT_MAX }; + } + + const float* start = &vertices[id * vertex_stride_floats(format) + normal_offset_floats(format)]; + return { *(start + 0), *(start + 1), *(start + 2) }; +} + +Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const +{ + const size_t t_stride = tex_coord_stride_floats(format); + if (t_stride != 2) { + assert(false); + return { FLT_MAX, FLT_MAX }; + } + + if (vertices_count() <= id) { + assert(false); + return { FLT_MAX, FLT_MAX }; + } + + const float* start = &vertices[id * vertex_stride_floats(format) + tex_coord_offset_floats(format)]; + return { *(start + 0), *(start + 1) }; +} + +void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal) +{ + assert(format.vertex_layout == EVertexLayout::P3N3); + assert(id < vertices_count()); + if (id < vertices_count()) { + float* start = &vertices[id * vertex_stride_floats(format)]; + *(start + 0) = position.x(); + *(start + 1) = position.y(); + *(start + 2) = position.z(); + *(start + 3) = normal.x(); + *(start + 4) = normal.y(); + *(start + 5) = normal.z(); + } +} + +void GLModel::Geometry::set_index(size_t id, unsigned int index) +{ + assert(id < indices_count()); + if (id < indices_count()) + indices[id] = index; +} + +unsigned int GLModel::Geometry::extract_index(size_t id) const +{ + if (indices_count() <= id) { + assert(false); + return -1; + } + + return indices[id]; +} + +void GLModel::Geometry::remove_vertex(size_t id) +{ + assert(id < vertices_count()); + if (id < vertices_count()) { + const size_t stride = vertex_stride_floats(format); + std::vector::const_iterator it = vertices.begin() + id * stride; + vertices.erase(it, it + stride); + } +} + +indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const +{ + indexed_triangle_set its; + its.vertices.reserve(vertices_count()); + for (size_t i = 0; i < vertices_count(); ++i) { + its.vertices.emplace_back(extract_position_3(i)); + } + its.indices.reserve(indices_count() / 3); + for (size_t i = 0; i < indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2)); + } + return its; +} + +size_t GLModel::Geometry::vertex_stride_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2: { return 2; } + case EVertexLayout::P2T2: { return 4; } + case EVertexLayout::P3: { return 3; } + case EVertexLayout::P3T2: { return 5; } + case EVertexLayout::P3N3: { return 6; } + case EVertexLayout::P3N3T2: { return 8; } + case EVertexLayout::P4: { return 4; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::position_stride_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2: + case EVertexLayout::P2T2: { return 2; } + case EVertexLayout::P3: + case EVertexLayout::P3T2: + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: { return 3; } + case EVertexLayout::P4: { return 4; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::position_offset_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2: + case EVertexLayout::P2T2: + case EVertexLayout::P3: + case EVertexLayout::P3T2: + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: + case EVertexLayout::P4: { return 0; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::normal_stride_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: { return 3; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::normal_offset_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: { return 3; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::tex_coord_stride_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2T2: + case EVertexLayout::P3T2: + case EVertexLayout::P3N3T2: { return 2; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::tex_coord_offset_floats(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2T2: { return 2; } + case EVertexLayout::P3T2: { return 3; } + case EVertexLayout::P3N3T2: { return 6; } + default: { assert(false); return 0; } + }; +} + +size_t GLModel::Geometry::index_stride_bytes(const Geometry& data) +{ + switch (data.index_type) + { + case EIndexType::UINT: { return sizeof(unsigned int); } + case EIndexType::USHORT: { return sizeof(unsigned short); } + case EIndexType::UBYTE: { return sizeof(unsigned char); } + default: { assert(false); return 0; } + }; +} + +bool GLModel::Geometry::has_position(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2: + case EVertexLayout::P2T2: + case EVertexLayout::P3: + case EVertexLayout::P3T2: + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: + case EVertexLayout::P4: { return true; } + default: { assert(false); return false; } + }; +} + +bool GLModel::Geometry::has_normal(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2: + case EVertexLayout::P2T2: + case EVertexLayout::P3: + case EVertexLayout::P3T2: + case EVertexLayout::P4: { return false; } + case EVertexLayout::P3N3: + case EVertexLayout::P3N3T2: { return true; } + default: { assert(false); return false; } + }; +} + +bool GLModel::Geometry::has_tex_coord(const Format& format) +{ + switch (format.vertex_layout) + { + case EVertexLayout::P2T2: + case EVertexLayout::P3T2: + case EVertexLayout::P3N3T2: { return true; } + case EVertexLayout::P2: + case EVertexLayout::P3: + case EVertexLayout::P3N3: + case EVertexLayout::P4: { return false; } + default: { assert(false); return false; } + }; +} + +void GLModel::init_from(Geometry&& data) +{ + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); return; + } - for (const InitializationData::Entity& entity : data.entities) { - if (entity.positions.empty() || entity.indices.empty()) - continue; + if (data.vertices.empty() || data.indices.empty()) { + assert(false); + return; + } - assert(entity.normals.empty() || entity.normals.size() == entity.positions.size()); + m_render_data.geometry = std::move(data); - RenderData rdata; - rdata.type = entity.type; - rdata.color = entity.color; - - // vertices/normals data - std::vector vertices(6 * entity.positions.size()); - for (size_t i = 0; i < entity.positions.size(); ++i) { - const size_t offset = i * 6; - ::memcpy(static_cast(&vertices[offset]), static_cast(entity.positions[i].data()), 3 * sizeof(float)); - if (!entity.normals.empty()) - ::memcpy(static_cast(&vertices[3 + offset]), static_cast(entity.normals[i].data()), 3 * sizeof(float)); + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + const size_t position_stride = Geometry::position_stride_floats(data.format); + if (position_stride == 3) + m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast()); + else if (position_stride == 2) { + const Vec2f position = m_render_data.geometry.extract_position_2(i); + m_bounding_box.merge(Vec3f(position.x(), position.y(), 0.0f).cast()); } - - // indices data - std::vector indices = entity.indices; - - rdata.indices_count = static_cast(indices.size()); - - // update bounding box - for (size_t i = 0; i < entity.positions.size(); ++i) { - m_bounding_box.merge(entity.positions[i].cast()); - } - - send_to_gpu(rdata, vertices, indices); - m_render_data.emplace_back(rdata); } } -void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox) +void GLModel::init_from(const TriangleMesh& mesh) { - if (!m_render_data.empty()) // call reset() if you want to reuse this model - return; - - RenderData data; - data.type = PrimitiveType::Triangles; - - std::vector vertices = std::vector(18 * its.indices.size()); - std::vector indices = std::vector(3 * its.indices.size()); - - unsigned int vertices_count = 0; - for (uint32_t i = 0; i < its.indices.size(); ++i) { - stl_triangle_vertex_indices face = its.indices[i]; - stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; - stl_vertex n = face_normal_normalized(vertex); - for (size_t j = 0; j < 3; ++ j) { - size_t offset = i * 18 + j * 6; - ::memcpy(static_cast(&vertices[offset]), static_cast(vertex[j].data()), 3 * sizeof(float)); - ::memcpy(static_cast(&vertices[3 + offset]), static_cast(n.data()), 3 * sizeof(float)); - } - for (size_t j = 0; j < 3; ++j) - indices[i * 3 + j] = vertices_count + j; - vertices_count += 3; - } - - data.indices_count = static_cast(indices.size()); - m_bounding_box = bbox; - - send_to_gpu(data, vertices, indices); - m_render_data.emplace_back(data); + init_from(mesh.its); } void GLModel::init_from(const indexed_triangle_set& its) { - this->init_from(its, bounding_box(its)); + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + if (its.vertices.empty() || its.indices.empty()){ + assert(false); + return; + } + + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(3 * its.indices.size()); + data.reserve_indices(3 * its.indices.size()); + + // vertices + indices + unsigned int vertices_counter = 0; + for (uint32_t i = 0; i < its.indices.size(); ++i) { + const stl_triangle_vertex_indices face = its.indices[i]; + const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; + const stl_vertex n = face_normal_normalized(vertex); + for (size_t j = 0; j < 3; ++j) { + data.add_vertex(vertex[j], n); + } + vertices_counter += 3; + data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(data.extract_position_3(i).cast()); + } } void GLModel::init_from(const Polygons& polygons, float z) { - auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) { - if (!polygon.empty()) { - GUI::GLModel::InitializationData::Entity entity; - entity.type = GUI::GLModel::PrimitiveType::LineLoop; - // contour - entity.positions.reserve(polygon.size() + 1); - entity.indices.reserve(polygon.size() + 1); - unsigned int id = 0; - for (const Point& p : polygon) { - Vec3f position = unscale(p.x(), p.y(), 0.0).cast(); - position.z() = z; - entity.positions.emplace_back(position); - entity.indices.emplace_back(id++); - } - data.entities.emplace_back(entity); - } - }; - - InitializationData init_data; - for (const Polygon& polygon : polygons) { - append_polygon(polygon, z, init_data); + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + if (polygons.empty()) { + assert(false); + return; + } + + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 }; + + size_t segments_count = 0; + for (const Polygon& polygon : polygons) { + segments_count += polygon.points.size(); + } + + data.reserve_vertices(2 * segments_count); + data.reserve_indices(2 * segments_count); + + // vertices + indices + unsigned int vertices_counter = 0; + for (const Polygon& poly : polygons) { + for (size_t i = 0; i < poly.points.size(); ++i) { + const Point& p0 = poly.points[i]; + const Point& p1 = (i == poly.points.size() - 1) ? poly.points.front() : poly.points[i + 1]; + data.add_vertex(Vec3f(unscale(p0.x()), unscale(p0.y()), z)); + data.add_vertex(Vec3f(unscale(p1.x()), unscale(p1.y()), z)); + vertices_counter += 2; + data.add_line(vertices_counter - 2, vertices_counter - 1); + } + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(data.extract_position_3(i).cast()); } - init_from(init_data); } bool GLModel::init_from_file(const std::string& filename) @@ -148,206 +530,342 @@ bool GLModel::init_from_file(const std::string& filename) return false; Model model; - try - { + try { model = Model::read_from_file(filename); } - catch (std::exception&) - { + catch (std::exception&) { return false; } - TriangleMesh mesh = model.mesh(); - init_from(mesh.its, mesh.bounding_box()); + init_from(model.mesh()); m_filename = filename; return true; } -void GLModel::set_color(int entity_id, const std::array& color) -{ - for (size_t i = 0; i < m_render_data.size(); ++i) { - if (entity_id == -1 || static_cast(i) == entity_id) - m_render_data[i].color = color; - } -} - void GLModel::reset() { - for (RenderData& data : m_render_data) { - // release gpu memory - if (data.ibo_id > 0) - glsafe(::glDeleteBuffers(1, &data.ibo_id)); - if (data.vbo_id > 0) - glsafe(::glDeleteBuffers(1, &data.vbo_id)); + // release gpu memory + if (m_render_data.ibo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_render_data.ibo_id)); + m_render_data.ibo_id = 0; + } + if (m_render_data.vbo_id > 0) { + glsafe(::glDeleteBuffers(1, &m_render_data.vbo_id)); + m_render_data.vbo_id = 0; } - m_render_data.clear(); + m_render_data.vertices_count = 0; + m_render_data.indices_count = 0; + m_render_data.geometry.vertices = std::vector(); + m_render_data.geometry.indices = std::vector(); m_bounding_box = BoundingBoxf3(); m_filename = std::string(); } -void GLModel::render() const +static GLenum get_primitive_mode(const GLModel::Geometry::Format& format) { - GLShaderProgram* shader = wxGetApp().get_current_shader(); - - for (const RenderData& data : m_render_data) { - if (data.vbo_id == 0 || data.ibo_id == 0) - continue; - - GLenum mode; - switch (data.type) - { - default: - case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } - case PrimitiveType::Lines: { mode = GL_LINES; break; } - case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } - case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } - } - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); - glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - - if (shader != nullptr) - shader->set_uniform("uniform_color", data.color); - else - glsafe(::glColor4fv(data.color.data())); - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); - glsafe(::glDrawElements(mode, static_cast(data.indices_count), GL_UNSIGNED_INT, (const void*)0)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + switch (format.type) + { + case GLModel::Geometry::EPrimitiveType::Points: { return GL_POINTS; } + default: + case GLModel::Geometry::EPrimitiveType::Triangles: { return GL_TRIANGLES; } + case GLModel::Geometry::EPrimitiveType::TriangleStrip: { return GL_TRIANGLE_STRIP; } + case GLModel::Geometry::EPrimitiveType::TriangleFan: { return GL_TRIANGLE_FAN; } + case GLModel::Geometry::EPrimitiveType::Lines: { return GL_LINES; } + case GLModel::Geometry::EPrimitiveType::LineStrip: { return GL_LINE_STRIP; } + case GLModel::Geometry::EPrimitiveType::LineLoop: { return GL_LINE_LOOP; } } } -void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const +static GLenum get_index_type(const GLModel::Geometry& data) { - if (instances_vbo == 0) + switch (data.index_type) + { + default: + case GLModel::Geometry::EIndexType::UINT: { return GL_UNSIGNED_INT; } + case GLModel::Geometry::EIndexType::USHORT: { return GL_UNSIGNED_SHORT; } + case GLModel::Geometry::EIndexType::UBYTE: { return GL_UNSIGNED_BYTE; } + } +} + +void GLModel::render() +{ + render(std::make_pair(0, indices_count())); +} + +void GLModel::render(const std::pair& range) +{ + if (m_render_disabled) + return; + + if (range.second == range.first) return; GLShaderProgram* shader = wxGetApp().get_current_shader(); - assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced")); + if (shader == nullptr) + return; - // vertex attributes - GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1; - GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1; - assert(position_id != -1 && normal_id != -1); - - // instance attributes - GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1; - GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1; - assert(offset_id != -1 && scales_id != -1); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo)); - if (offset_id != -1) { - glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0)); - glsafe(::glEnableVertexAttribArray(offset_id)); - glsafe(::glVertexAttribDivisor(offset_id, 1)); - } - if (scales_id != -1) { - glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); - glsafe(::glEnableVertexAttribArray(scales_id)); - glsafe(::glVertexAttribDivisor(scales_id, 1)); + // sends data to gpu if not done yet + if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { + if (m_render_data.geometry.vertices_count() > 0 && m_render_data.geometry.indices_count() > 0 && !send_to_gpu()) + return; } - for (const RenderData& data : m_render_data) { - if (data.vbo_id == 0 || data.ibo_id == 0) - continue; + const Geometry& data = m_render_data.geometry; - GLenum mode; - switch (data.type) - { - default: - case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } - case PrimitiveType::Lines: { mode = GL_LINES; break; } - case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } - case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } - } + const GLenum mode = get_primitive_mode(data.format); + const GLenum index_type = get_index_type(data); - if (shader != nullptr) - shader->set_uniform("uniform_color", data.color); - else - glsafe(::glColor4fv(data.color.data())); + const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); + const bool position = Geometry::has_position(data.format); + const bool normal = Geometry::has_normal(data.format); + const bool tex_coord = Geometry::has_tex_coord(data.format); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); + + int position_id = -1; + int normal_id = -1; + int tex_coord_id = -1; + + if (position) { + position_id = shader->get_attrib_location("v_position"); if (position_id != -1) { - glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0)); + glsafe(::glVertexAttribPointer(position_id, Geometry::position_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(position_id)); } + } + if (normal) { + normal_id = shader->get_attrib_location("v_normal"); if (normal_id != -1) { - glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); + glsafe(::glVertexAttribPointer(normal_id, Geometry::normal_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); glsafe(::glEnableVertexAttribArray(normal_id)); } - - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); - glsafe(::glDrawElementsInstanced(mode, static_cast(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - - if (normal_id != -1) - glsafe(::glDisableVertexAttribArray(normal_id)); - if (position_id != -1) - glsafe(::glDisableVertexAttribArray(position_id)); + } + if (tex_coord) { + tex_coord_id = shader->get_attrib_location("v_tex_coord"); + if (tex_coord_id != -1) { + glsafe(::glVertexAttribPointer(tex_coord_id, Geometry::tex_coord_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::tex_coord_offset_bytes(data.format))); + glsafe(::glEnableVertexAttribArray(tex_coord_id)); + } } - if (scales_id != -1) - glsafe(::glDisableVertexAttribArray(scales_id)); - if (offset_id != -1) - glsafe(::glDisableVertexAttribArray(offset_id)); + shader->set_uniform("uniform_color", data.color); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); -} - -void GLModel::send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices) -{ - assert(data.vbo_id == 0); - assert(data.ibo_id == 0); - - // vertex data -> send to gpu - glsafe(::glGenBuffers(1, &data.vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - - // indices data -> send to gpu - glsafe(::glGenBuffers(1, &data.ibo_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); + glsafe(::glDrawElements(mode, range.second - range.first, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data)))); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (tex_coord_id != -1) + glsafe(::glDisableVertexAttribArray(tex_coord_id)); + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } -GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) +void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) { - auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { - entity.positions.emplace_back(position); - entity.normals.emplace_back(normal); - }; - auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { - entity.indices.emplace_back(v1); - entity.indices.emplace_back(v2); - entity.indices.emplace_back(v3); - }; + if (instances_vbo == 0 || instances_count == 0) + return; - resolution = std::max(4, resolution); + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr || !boost::algorithm::iends_with(shader->get_name(), "_instanced")) + return; - GLModel::InitializationData data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; + // vertex attributes + const GLint position_id = shader->get_attrib_location("v_position"); + const GLint normal_id = shader->get_attrib_location("v_normal"); + if (position_id == -1 || normal_id == -1) + return; - const float angle_step = 2.0f * M_PI / static_cast(resolution); + // instance attributes + const GLint offset_id = shader->get_attrib_location("i_offset"); + const GLint scales_id = shader->get_attrib_location("i_scales"); + if (offset_id == -1 || scales_id == -1) + return; + + if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { + if (!send_to_gpu()) + return; + } + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo)); + const size_t instance_stride = 5 * sizeof(float); + glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, instance_stride, (const void*)0)); + glsafe(::glEnableVertexAttribArray(offset_id)); + glsafe(::glVertexAttribDivisor(offset_id, 1)); + + glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, instance_stride, (const void*)(3 * sizeof(float)))); + glsafe(::glEnableVertexAttribArray(scales_id)); + glsafe(::glVertexAttribDivisor(scales_id, 1)); + + const Geometry& data = m_render_data.geometry; + + const GLenum mode = get_primitive_mode(data.format); + const GLenum index_type = get_index_type(data); + + const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); + const bool position = Geometry::has_position(data.format); + const bool normal = Geometry::has_normal(data.format); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); + + if (position) { + glsafe(::glVertexAttribPointer(position_id, Geometry::position_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); + glsafe(::glEnableVertexAttribArray(position_id)); + } + + if (normal) { + glsafe(::glVertexAttribPointer(normal_id, Geometry::normal_stride_floats(data.format), GL_FLOAT, GL_FALSE, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); + glsafe(::glEnableVertexAttribArray(normal_id)); + } + + shader->set_uniform("uniform_color", data.color); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); + glsafe(::glDrawElementsInstanced(mode, indices_count(), index_type, (const void*)0, instances_count)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (normal) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position) + glsafe(::glDisableVertexAttribArray(position_id)); + + glsafe(::glDisableVertexAttribArray(scales_id)); + glsafe(::glDisableVertexAttribArray(offset_id)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +bool GLModel::send_to_gpu() +{ + if (m_render_data.vbo_id > 0 || m_render_data.ibo_id > 0) { + assert(false); + return false; + } + + Geometry& data = m_render_data.geometry; + if (data.vertices.empty() || data.indices.empty()) { + assert(false); + return false; + } + + // vertices + glsafe(::glGenBuffers(1, &m_render_data.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, data.vertices_size_bytes(), data.vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + m_render_data.vertices_count = vertices_count(); + data.vertices = std::vector(); + + // indices + glsafe(::glGenBuffers(1, &m_render_data.ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); + const size_t indices_count = data.indices.size(); + if (m_render_data.vertices_count <= 256) { + // convert indices to unsigned char to save gpu memory + std::vector reduced_indices(indices_count); + for (size_t i = 0; i < indices_count; ++i) { + reduced_indices[i] = (unsigned char)data.indices[i]; + } + data.index_type = Geometry::EIndexType::UBYTE; + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(unsigned char), reduced_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + else if (m_render_data.vertices_count <= 65536) { + // convert indices to unsigned short to save gpu memory + std::vector reduced_indices(indices_count); + for (size_t i = 0; i < data.indices.size(); ++i) { + reduced_indices[i] = (unsigned short)data.indices[i]; + } + data.index_type = Geometry::EIndexType::USHORT; + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(unsigned short), reduced_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + else { + data.index_type = Geometry::EIndexType::UINT; + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indices_size_bytes(), data.indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + m_render_data.indices_count = indices_count; + data.indices = std::vector(); + + return true; +} + +template +inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn) +{ + const size_t position_stride_floats = geometry.position_stride_floats(geometry.format); + const size_t position_offset_floats = geometry.position_offset_floats(geometry.format); + assert(position_stride_floats == 3); + if (geometry.vertices.empty() || position_stride_floats != 3) + return false; + + for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) { + it += position_offset_floats; + if (!fn({ *it, *(it + 1), *(it + 2) })) + return false; + it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats); + } + return true; +} + +bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom) +{ + static constexpr const double epsilon = BuildVolume::BedEpsilon; + switch (volume.type()) { + case BuildVolume_Type::Rectangle: + { + BoundingBox3Base build_volume = volume.bounding_volume().inflated(epsilon); + if (volume.printable_height() == 0.0) + build_volume.max.z() = std::numeric_limits::max(); + if (ignore_bottom) + build_volume.min.z() = -std::numeric_limits::max(); + const BoundingBoxf3& model_box = model.get_bounding_box(); + return build_volume.contains(model_box.min) && build_volume.contains(model_box.max); + } + case BuildVolume_Type::Circle: + { + const Geometry::Circled& circle = volume.circle(); + const Vec2f c = unscaled(circle.center); + const float r = unscaled(circle.radius) + float(epsilon); + const float r2 = sqr(r); + return volume.printable_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) : + + all_vertices_inside(model.get_geometry(), [c, r2, z = volume.printable_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; }); + } + case BuildVolume_Type::Convex: + //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. + case BuildVolume_Type::Custom: + return volume.printable_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast()); }) : + all_vertices_inside(model.get_geometry(), [&volume, z = volume.printable_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast()) && p.z() <= z; }); + default: + return true; + } +} + +GLModel::Geometry stilized_arrow(unsigned int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) +{ + resolution = std::max(4, resolution); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(6 * resolution + 2); + data.reserve_indices(6 * resolution * 3); + + const float angle_step = 2.0f * float(PI) / float(resolution); std::vector cosines(resolution); std::vector sines(resolution); - for (int i = 0; i < resolution; ++i) { - const float angle = angle_step * static_cast(i); + for (unsigned int i = 0; i < resolution; ++i) { + const float angle = angle_step * float(i); cosines[i] = ::cos(angle); sines[i] = -::sin(angle); } @@ -355,86 +873,76 @@ GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, flo const float total_height = tip_height + stem_height; // tip vertices/normals - append_vertex(entity, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + data.add_vertex(Vec3f(0.0f, 0.0f, total_height), (Vec3f)Vec3f::UnitZ()); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(tip_radius * sines[i], tip_radius * cosines[i], stem_height), Vec3f(sines[i], cosines[i], 0.0f)); } // tip triangles - for (int i = 0; i < resolution; ++i) { - const int v3 = (i < resolution - 1) ? i + 2 : 1; - append_indices(entity, 0, i + 1, v3); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int v3 = (i < resolution - 1) ? i + 2 : 1; + data.add_triangle(0, i + 1, v3); } // tip cap outer perimeter vertices - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(tip_radius * sines[i], tip_radius * cosines[i], stem_height), (Vec3f)(-Vec3f::UnitZ())); } // tip cap inner perimeter vertices - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], stem_height), (Vec3f)(-Vec3f::UnitZ())); } // tip cap triangles - for (int i = 0; i < resolution; ++i) { - const int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; - const int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; - append_indices(entity, i + resolution + 1, v3, v2); - append_indices(entity, i + resolution + 1, i + 2 * resolution + 1, v3); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; + const unsigned int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; + data.add_triangle(i + resolution + 1, v3, v2); + data.add_triangle(i + resolution + 1, i + 2 * resolution + 1, v3); } // stem bottom vertices - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], stem_height), Vec3f(sines[i], cosines[i], 0.0f)); } // stem top vertices - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], 0.0f), Vec3f(sines[i], cosines[i], 0.0f)); } // stem triangles - for (int i = 0; i < resolution; ++i) { - const int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; - const int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; - append_indices(entity, i + 3 * resolution + 1, v3, v2); - append_indices(entity, i + 3 * resolution + 1, i + 4 * resolution + 1, v3); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; + const unsigned int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; + data.add_triangle(i + 3 * resolution + 1, v3, v2); + data.add_triangle(i + 3 * resolution + 1, i + 4 * resolution + 1, v3); } // stem cap vertices - append_vertex(entity, Vec3f::Zero(), -Vec3f::UnitZ()); - for (int i = 0; i < resolution; ++i) { - append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); + data.add_vertex((Vec3f)Vec3f::Zero(), (Vec3f)(-Vec3f::UnitZ())); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_vertex(Vec3f(stem_radius * sines[i], stem_radius * cosines[i], 0.0f), (Vec3f)(-Vec3f::UnitZ())); } // stem cap triangles - for (int i = 0; i < resolution; ++i) { - const int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; - append_indices(entity, 5 * resolution + 1, v3, i + 5 * resolution + 2); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; + data.add_triangle(5 * resolution + 1, v3, i + 5 * resolution + 2); } - data.entities.emplace_back(entity); return data; } -GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) +GLModel::Geometry circular_arrow(unsigned int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) { - auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { - entity.positions.emplace_back(position); - entity.normals.emplace_back(normal); - }; - auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { - entity.indices.emplace_back(v1); - entity.indices.emplace_back(v2); - entity.indices.emplace_back(v3); - }; + resolution = std::max(2, resolution); - resolution = std::max(2, resolution); - - GLModel::InitializationData data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(8 * (resolution + 1) + 30); + data.reserve_indices((8 * resolution + 16) * 3); const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; @@ -442,171 +950,161 @@ GLModel::InitializationData circular_arrow(int resolution, float radius, float t const float outer_radius = radius + half_stem_width; const float inner_radius = radius - half_stem_width; - const float step_angle = 0.5f * PI / static_cast(resolution); + const float step_angle = 0.5f * float(PI) / float(resolution); // tip // top face vertices - append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, outer_radius, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(-tip_height, radius, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, inner_radius, half_thickness), (Vec3f)Vec3f::UnitZ()); // top face triangles - append_indices(entity, 0, 1, 2); - append_indices(entity, 0, 2, 4); - append_indices(entity, 4, 2, 3); + data.add_triangle(0, 1, 2); + data.add_triangle(0, 2, 4); + data.add_triangle(4, 2, 3); // bottom face vertices - append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, outer_radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(0.0f, inner_radius, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); // bottom face triangles - append_indices(entity, 5, 7, 6); - append_indices(entity, 5, 9, 7); - append_indices(entity, 9, 8, 7); + data.add_triangle(5, 7, 6); + data.add_triangle(5, 9, 7); + data.add_triangle(9, 8, 7); // side faces vertices - append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, outer_radius, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, outer_radius, half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), (Vec3f)Vec3f::UnitX()); Vec3f normal(-half_tip_width, tip_height, 0.0f); normal.normalize(); - append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, normal); - append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); - append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, normal); - append_vertex(entity, { -tip_height, radius, half_thickness }, normal); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, -half_thickness), normal); + data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), normal); + data.add_vertex(Vec3f(0.0f, radius + half_tip_width, half_thickness), normal); + data.add_vertex(Vec3f(-tip_height, radius, half_thickness), normal); - normal = Vec3f(-half_tip_width, -tip_height, 0.0f); + normal = { -half_tip_width, -tip_height, 0.0f }; normal.normalize(); - append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); - append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, normal); - append_vertex(entity, { -tip_height, radius, half_thickness }, normal); - append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, normal); + data.add_vertex(Vec3f(-tip_height, radius, -half_thickness), normal); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), normal); + data.add_vertex(Vec3f(-tip_height, radius, half_thickness), normal); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), normal); - append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, inner_radius, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, radius - half_tip_width, half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(0.0f, inner_radius, half_thickness), (Vec3f)Vec3f::UnitX()); // side face triangles - for (int i = 0; i < 4; ++i) { - const int ii = i * 4; - append_indices(entity, 10 + ii, 11 + ii, 13 + ii); - append_indices(entity, 10 + ii, 13 + ii, 12 + ii); + for (unsigned int i = 0; i < 4; ++i) { + const unsigned int ii = i * 4; + data.add_triangle(10 + ii, 11 + ii, 13 + ii); + data.add_triangle(10 + ii, 13 + ii, 12 + ii); } // stem // top face vertices - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; - append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; + data.add_vertex(Vec3f(inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness), (Vec3f)Vec3f::UnitZ()); } - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; - append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; + data.add_vertex(Vec3f(outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness), (Vec3f)Vec3f::UnitZ()); } // top face triangles - for (int i = 0; i < resolution; ++i) { - append_indices(entity, 26 + i, 27 + i, 27 + resolution + i); - append_indices(entity, 27 + i, 28 + resolution + i, 27 + resolution + i); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_triangle(26 + i, 27 + i, 27 + resolution + i); + data.add_triangle(27 + i, 28 + resolution + i, 27 + resolution + i); } // bottom face vertices - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; - append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; + data.add_vertex(Vec3f(inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness), (Vec3f)(-Vec3f::UnitZ())); } - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; - append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; + data.add_vertex(Vec3f(outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness), (Vec3f)(-Vec3f::UnitZ())); } // bottom face triangles - for (int i = 0; i < resolution; ++i) { - append_indices(entity, 28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); - append_indices(entity, 29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_triangle(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); + data.add_triangle(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); } // side faces vertices and triangles - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); - append_vertex(entity, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); + data.add_vertex(Vec3f(inner_radius * s, inner_radius * c, -half_thickness), Vec3f(-s, -c, 0.0f)); } - for (int i = 0; i <= resolution; ++i) { - const float angle = static_cast(i) * step_angle; + for (unsigned int i = 0; i <= resolution; ++i) { + const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); - append_vertex(entity, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); + data.add_vertex(Vec3f(inner_radius * s, inner_radius * c, half_thickness), Vec3f(-s, -c, 0.0f)); } - int first_id = 26 + 4 * (resolution + 1); - for (int i = 0; i < resolution; ++i) { - const int ii = first_id + i; - append_indices(entity, ii, ii + 1, ii + resolution + 2); - append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); + unsigned int first_id = 26 + 4 * (resolution + 1); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int ii = first_id + i; + data.add_triangle(ii, ii + 1, ii + resolution + 2); + data.add_triangle(ii, ii + resolution + 2, ii + resolution + 1); } - append_vertex(entity, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + data.add_vertex(Vec3f(inner_radius, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(outer_radius, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(inner_radius, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(outer_radius, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); first_id = 26 + 6 * (resolution + 1); - append_indices(entity, first_id, first_id + 1, first_id + 3); - append_indices(entity, first_id, first_id + 3, first_id + 2); + data.add_triangle(first_id, first_id + 1, first_id + 3); + data.add_triangle(first_id, first_id + 3, first_id + 2); for (int i = resolution; i >= 0; --i) { - const float angle = static_cast(i) * step_angle; + const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); - append_vertex(entity, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); + data.add_vertex(Vec3f(outer_radius * s, outer_radius * c, -half_thickness), Vec3f(s, c, 0.0f)); } for (int i = resolution; i >= 0; --i) { - const float angle = static_cast(i) * step_angle; + const float angle = float(i) * step_angle; const float c = ::cos(angle); const float s = ::sin(angle); - append_vertex(entity, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); + data.add_vertex(Vec3f(outer_radius * s, outer_radius * c, +half_thickness), Vec3f(s, c, 0.0f)); } first_id = 30 + 6 * (resolution + 1); - for (int i = 0; i < resolution; ++i) { - const int ii = first_id + i; - append_indices(entity, ii, ii + 1, ii + resolution + 2); - append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); + for (unsigned int i = 0; i < resolution; ++i) { + const unsigned int ii = first_id + i; + data.add_triangle(ii, ii + 1, ii + resolution + 2); + data.add_triangle(ii, ii + resolution + 2, ii + resolution + 1); } - data.entities.emplace_back(entity); return data; } -GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) +GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) { - auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { - entity.positions.emplace_back(position); - entity.normals.emplace_back(normal); - }; - auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { - entity.indices.emplace_back(v1); - entity.indices.emplace_back(v2); - entity.indices.emplace_back(v3); - }; - - GLModel::InitializationData data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(42); + data.reserve_indices(72); const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; @@ -614,133 +1112,307 @@ GLModel::InitializationData straight_arrow(float tip_width, float tip_height, fl const float total_height = tip_height + stem_height; // top face vertices - append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); - append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(0.0f, total_height, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitZ()); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitZ()); // top face triangles - append_indices(entity, 0, 1, 6); - append_indices(entity, 6, 1, 5); - append_indices(entity, 4, 5, 3); - append_indices(entity, 5, 1, 3); - append_indices(entity, 1, 2, 3); + data.add_triangle(0, 1, 6); + data.add_triangle(6, 1, 5); + data.add_triangle(4, 5, 3); + data.add_triangle(5, 1, 3); + data.add_triangle(1, 2, 3); // bottom face vertices - append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); - append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitZ())); // bottom face triangles - append_indices(entity, 7, 13, 8); - append_indices(entity, 13, 12, 8); - append_indices(entity, 12, 11, 10); - append_indices(entity, 8, 12, 10); - append_indices(entity, 9, 8, 10); + data.add_triangle(7, 13, 8); + data.add_triangle(13, 12, 8); + data.add_triangle(12, 11, 10); + data.add_triangle(8, 12, 10); + data.add_triangle(9, 8, 10); // side faces vertices - append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); - append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); + data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)Vec3f::UnitX()); + data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)Vec3f::UnitX()); - append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + data.add_vertex(Vec3f(half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); Vec3f normal(tip_height, half_tip_width, 0.0f); normal.normalize(); - append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, normal); - append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); - append_vertex(entity, { half_tip_width, stem_height, half_thickness }, normal); - append_vertex(entity, { 0.0, total_height, half_thickness }, normal); + data.add_vertex(Vec3f(half_tip_width, stem_height, -half_thickness), normal); + data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), normal); + data.add_vertex(Vec3f(half_tip_width, stem_height, half_thickness), normal); + data.add_vertex(Vec3f(0.0f, total_height, half_thickness), normal); - normal = Vec3f(-tip_height, half_tip_width, 0.0f); + normal = { -tip_height, half_tip_width, 0.0f }; normal.normalize(); - append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); - append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, normal); - append_vertex(entity, { 0.0, total_height, half_thickness }, normal); - append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, normal); + data.add_vertex(Vec3f(0.0f, total_height, -half_thickness), normal); + data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), normal); + data.add_vertex(Vec3f(0.0f, total_height, half_thickness), normal); + data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), normal); - append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + data.add_vertex(Vec3f(-half_tip_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(-half_tip_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitY())); - append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); - append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); - append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); - append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); + data.add_vertex(Vec3f(-half_stem_width, stem_height, -half_thickness), (Vec3f)(-Vec3f::UnitX())); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitX())); + data.add_vertex(Vec3f(-half_stem_width, stem_height, half_thickness), (Vec3f)(-Vec3f::UnitX())); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitX())); - append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); - append_vertex(entity, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(half_stem_width, 0.0f, -half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(-half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); + data.add_vertex(Vec3f(half_stem_width, 0.0f, half_thickness), (Vec3f)(-Vec3f::UnitY())); // side face triangles - for (int i = 0; i < 7; ++i) { - const int ii = i * 4; - append_indices(entity, 14 + ii, 15 + ii, 17 + ii); - append_indices(entity, 14 + ii, 17 + ii, 16 + ii); + for (unsigned int i = 0; i < 7; ++i) { + const unsigned int ii = i * 4; + data.add_triangle(14 + ii, 15 + ii, 17 + ii); + data.add_triangle(14 + ii, 17 + ii, 16 + ii); } - data.entities.emplace_back(entity); return data; } -GLModel::InitializationData diamond(int resolution) +GLModel::Geometry diamond(unsigned int resolution) { - resolution = std::max(4, resolution); + resolution = std::max(4, resolution); - GLModel::InitializationData data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(resolution + 2); + data.reserve_indices((2 * (resolution + 1)) * 3); const float step = 2.0f * float(PI) / float(resolution); - // positions - for (int i = 0; i < resolution; ++i) { - float ii = float(i) * step; - entity.positions.emplace_back(0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f); - } - entity.positions.emplace_back(0.0f, 0.0f, 0.5f); - entity.positions.emplace_back(0.0f, 0.0f, -0.5f); - - // normals - for (const Vec3f& v : entity.positions) { - entity.normals.emplace_back(v.normalized()); + // vertices + for (unsigned int i = 0; i < resolution; ++i) { + const float ii = float(i) * step; + const Vec3f p = { 0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f }; + data.add_vertex(p, (Vec3f)p.normalized()); } + Vec3f p = { 0.0f, 0.0f, 0.5f }; + data.add_vertex(p, (Vec3f)p.normalized()); + p = { 0.0f, 0.0f, -0.5f }; + data.add_vertex(p, (Vec3f)p.normalized()); // triangles // top - for (int i = 0; i < resolution; ++i) { - entity.indices.push_back(i + 0); - entity.indices.push_back(i + 1); - entity.indices.push_back(resolution); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_triangle(i + 0, i + 1, resolution); } - entity.indices.push_back(resolution - 1); - entity.indices.push_back(0); - entity.indices.push_back(resolution); + data.add_triangle(resolution - 1, 0, resolution); // bottom - for (int i = 0; i < resolution; ++i) { - entity.indices.push_back(i + 0); - entity.indices.push_back(resolution + 1); - entity.indices.push_back(i + 1); + for (unsigned int i = 0; i < resolution; ++i) { + data.add_triangle(i + 0, resolution + 1, i + 1); + } + data.add_triangle(resolution - 1, resolution + 1, 0); + + return data; +} + +GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) +{ + resolution = std::max(4, resolution); + + const unsigned int sectorCount = resolution; + const unsigned int stackCount = resolution; + + const float sectorStep = float(2.0 * M_PI / sectorCount); + const float stackStep = float(M_PI / stackCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices((stackCount - 1) * sectorCount + 2); + data.reserve_indices((2 * (stackCount - 1) * sectorCount) * 3); + + // vertices + for (unsigned int i = 0; i <= stackCount; ++i) { + // from pi/2 to -pi/2 + const double stackAngle = 0.5 * M_PI - stackStep * i; + const double xy = double(radius) * ::cos(stackAngle); + const double z = double(radius) * ::sin(stackAngle); + if (i == 0 || i == stackCount) { + const Vec3f v(float(xy), 0.0f, float(z)); + data.add_vertex(v, (Vec3f)v.normalized()); + } + else { + for (unsigned int j = 0; j < sectorCount; ++j) { + // from 0 to 2pi + const double sectorAngle = sectorStep * j; + const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z)); + data.add_vertex(v, (Vec3f)v.normalized()); + } + } + } + + // triangles + for (unsigned int i = 0; i < stackCount; ++i) { + // Beginning of current stack. + unsigned int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); + const unsigned int k1_first = k1; + // Beginning of next stack. + unsigned int k2 = (i == 0) ? 1 : (k1 + sectorCount); + const unsigned int k2_first = k2; + for (unsigned int j = 0; j < sectorCount; ++j) { + // 2 triangles per sector excluding first and last stacks + unsigned int k1_next = k1; + unsigned int k2_next = k2; + if (i != 0) { + k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); + data.add_triangle(k1, k2, k1_next); + } + if (i + 1 != stackCount) { + k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); + data.add_triangle(k1_next, k2, k2_next); + } + k1 = k1_next; + k2 = k2_next; + } + } + + return data; +} + +GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height) +{ + resolution = std::max(4, resolution); + + const unsigned int sectorCount = resolution; + const float sectorStep = 2.0f * float(M_PI) / float(sectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(sectorCount * 4 + 2); + data.reserve_indices(sectorCount * 4 * 3); + + auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) { + std::vector ret; + ret.reserve(sectorCount); + for (unsigned int i = 0; i < sectorCount; ++i) { + // from 0 to 2pi + const float sectorAngle = sectorStep * i; + ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); + } + return ret; + }; + + const std::vector base_vertices = generate_vertices_on_circle(radius); + const Vec3f h = height * Vec3f::UnitZ(); + + // stem vertices + for (unsigned int i = 0; i < sectorCount; ++i) { + const Vec3f& v = base_vertices[i]; + const Vec3f n = v.normalized(); + data.add_vertex(v, n); + data.add_vertex(v + h, n); + } + + // stem triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + unsigned int v1 = i * 2; + unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0; + unsigned int v3 = v2 + 1; + unsigned int v4 = v1 + 1; + data.add_triangle(v1, v2, v3); + data.add_triangle(v1, v3, v4); + } + + // bottom cap vertices + Vec3f cap_center = Vec3f::Zero(); + unsigned int cap_center_id = data.vertices_count(); + Vec3f normal = -Vec3f::UnitZ(); + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i], normal); + } + + // bottom cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1); + } + + // top cap vertices + cap_center += h; + cap_center_id = data.vertices_count(); + normal = -normal; + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i] + h, normal); + } + + // top cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1); + } + + return data; +} + +GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness) +{ + const unsigned int torus_sector_count = std::max(4, primary_resolution); + const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); + const unsigned int section_sector_count = std::max(4, secondary_resolution); + const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torus_sector_count * section_sector_count); + data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); + + // vertices + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const float section_angle = torus_sector_step * i; + const float csa = std::cos(section_angle); + const float ssa = std::sin(section_angle); + const Vec3f section_center(radius * csa, radius * ssa, 0.0f); + for (unsigned int j = 0; j < section_sector_count; ++j) { + const float circle_angle = section_sector_step * j; + const float thickness_xy = thickness * std::cos(circle_angle); + const float thickness_z = thickness * std::sin(circle_angle); + const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z); + data.add_vertex(section_center + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const unsigned int ii = i * section_sector_count; + const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const unsigned int j_next = (j + 1) % section_sector_count; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } } - entity.indices.push_back(resolution - 1); - entity.indices.push_back(resolution + 1); - entity.indices.push_back(0); - data.entities.emplace_back(entity); return data; } diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index d47c56fd93..e90c263ece 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -1,8 +1,13 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLModel_hpp_ #define slic3r_GLModel_hpp_ #include "libslic3r/Point.hpp" #include "libslic3r/BoundingBox.hpp" +#include "libslic3r/Color.hpp" #include #include @@ -13,53 +18,139 @@ namespace Slic3r { class TriangleMesh; class Polygon; using Polygons = std::vector; +class BuildVolume; namespace GUI { class GLModel { public: - enum class PrimitiveType : unsigned char + struct Geometry { - Triangles, - Lines, - LineStrip, - LineLoop + enum class EPrimitiveType : unsigned char + { + Points, + Triangles, + TriangleStrip, + TriangleFan, + Lines, + LineStrip, + LineLoop + }; + + enum class EVertexLayout : unsigned char + { + P2, // position 2 floats + P2T2, // position 2 floats + texture coords 2 floats + P3, // position 3 floats + P3T2, // position 3 floats + texture coords 2 floats + P3N3, // position 3 floats + normal 3 floats + P3N3T2, // position 3 floats + normal 3 floats + texture coords 2 floats + P4, // position 4 floats + }; + + enum class EIndexType : unsigned char + { + UINT, // unsigned int + USHORT, // unsigned short + UBYTE // unsigned byte + }; + + struct Format + { + EPrimitiveType type{ EPrimitiveType::Triangles }; + EVertexLayout vertex_layout{ EVertexLayout::P3N3 }; + }; + + Format format; + std::vector vertices; + std::vector indices; + EIndexType index_type{ EIndexType::UINT }; + ColorRGBA color{ ColorRGBA::BLACK() }; + + void reserve_vertices(size_t vertices_count) { vertices.reserve(vertices_count * vertex_stride_floats(format)); } + void reserve_indices(size_t indices_count) { indices.reserve(indices_count); } + + void add_vertex(const Vec2f& position); // EVertexLayout::P2 + void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2 + void add_vertex(const Vec3f& position); // EVertexLayout::P3 + void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2 + void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 + void add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord); // EVertexLayout::P3N3T2 + void add_vertex(const Vec4f& position); // EVertexLayout::P4 + + void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 + + void set_index(size_t id, unsigned int index); + + void add_index(unsigned int id); + void add_line(unsigned int id1, unsigned int id2); + void add_triangle(unsigned int id1, unsigned int id2, unsigned int id3); + + Vec2f extract_position_2(size_t id) const; + Vec3f extract_position_3(size_t id) const; + Vec3f extract_normal_3(size_t id) const; + Vec2f extract_tex_coord_2(size_t id) const; + + unsigned int extract_index(size_t id) const; + + void remove_vertex(size_t id); + + bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; } + + size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); } + size_t indices_count() const { return indices.size(); } + + size_t vertices_size_floats() const { return vertices.size(); } + size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } + size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); } + + indexed_triangle_set get_as_indexed_triangle_set() const; + + static size_t vertex_stride_floats(const Format& format); + static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); } + + static size_t position_stride_floats(const Format& format); + static size_t position_stride_bytes(const Format& format) { return position_stride_floats(format) * sizeof(float); } + static size_t position_offset_floats(const Format& format); + static size_t position_offset_bytes(const Format& format) { return position_offset_floats(format) * sizeof(float); } + + static size_t normal_stride_floats(const Format& format); + static size_t normal_stride_bytes(const Format& format) { return normal_stride_floats(format) * sizeof(float); } + static size_t normal_offset_floats(const Format& format); + static size_t normal_offset_bytes(const Format& format) { return normal_offset_floats(format) * sizeof(float); } + + static size_t tex_coord_stride_floats(const Format& format); + static size_t tex_coord_stride_bytes(const Format& format) { return tex_coord_stride_floats(format) * sizeof(float); } + static size_t tex_coord_offset_floats(const Format& format); + static size_t tex_coord_offset_bytes(const Format& format) { return tex_coord_offset_floats(format) * sizeof(float); } + + static size_t index_stride_bytes(const Geometry& data); + + static bool has_position(const Format& format); + static bool has_normal(const Format& format); + static bool has_tex_coord(const Format& format); }; struct RenderData { - PrimitiveType type; + Geometry geometry; unsigned int vbo_id{ 0 }; unsigned int ibo_id{ 0 }; + size_t vertices_count{ 0 }; size_t indices_count{ 0 }; - std::array color{ 1.0f, 1.0f, 1.0f, 1.0f }; }; - - struct InitializationData - { - struct Entity - { - PrimitiveType type; - std::vector positions; - std::vector normals; - std::vector indices; - std::array color{ 1.0f, 1.0f, 1.0f, 1.0f }; - }; - - std::vector entities; - - size_t vertices_count() const; - size_t vertices_size_floats() const { return vertices_count() * 6; } - size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } - - size_t indices_count() const; - size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); } - }; - private: - std::vector m_render_data; + RenderData m_render_data; + // By default the vertex and index buffers data are sent to gpu at the first call to render() method. + // If you need to initialize a model from outside the main thread, so that a call to render() may happen + // before the initialization is complete, use the methods: + // disable_render() + // ... do your initialization ... + // enable_render() + // to keep the data on cpu side until needed. + bool m_render_disabled{ false }; BoundingBoxf3 m_bounding_box; std::string m_filename; @@ -67,50 +158,98 @@ namespace GUI { GLModel() = default; virtual ~GLModel() { reset(); } - void init_from(const InitializationData& data); - void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox); + size_t vertices_count() const { return m_render_data.vertices_count > 0 ? + m_render_data.vertices_count : m_render_data.geometry.vertices_count(); } + size_t indices_count() const { return m_render_data.indices_count > 0 ? + m_render_data.indices_count : m_render_data.geometry.indices_count(); } + + size_t vertices_size_floats() const { return vertices_count() * Geometry::vertex_stride_floats(m_render_data.geometry.format); } + size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } + + size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry); } + + const Geometry& get_geometry() const { return m_render_data.geometry; } + + void init_from(Geometry&& data); + void init_from(const TriangleMesh& mesh); void init_from(const indexed_triangle_set& its); void init_from(const Polygons& polygons, float z); bool init_from_file(const std::string& filename); - // if entity_id == -1 set the color of all entities - void set_color(int entity_id, const std::array& color); + void set_color(const ColorRGBA& color) { m_render_data.geometry.color = color; } + const ColorRGBA& get_color() const { return m_render_data.geometry.color; } void reset(); - void render() const; - void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const; + void render(); + void render(const std::pair& range); + void render_instanced(unsigned int instances_vbo, unsigned int instances_count); - bool is_initialized() const { return !m_render_data.empty(); } + bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } + bool is_empty() const { return m_render_data.geometry.is_empty(); } const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::string& get_filename() const { return m_filename; } + bool is_render_disabled() const { return m_render_disabled; } + void enable_render() { m_render_disabled = false; } + void disable_render() { m_render_disabled = true; } + + size_t cpu_memory_used() const { + size_t ret = 0; + if (!m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (!m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } + size_t gpu_memory_used() const { + size_t ret = 0; + if (m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } + private: - void send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices); + bool send_to_gpu(); }; + bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true); // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution // the origin of the arrow is in the center of the stem cap // the arrow has its axis of symmetry along the Z axis and is pointing upward // used to render bed axes and sequential marker - GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); + GLModel::Geometry stilized_arrow(unsigned int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution // the origin of the arrow is in the center of the circle // the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise // used to render sidebar hints for rotations - GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); + GLModel::Geometry circular_arrow(unsigned int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); // create an arrow with the given dimensions // the origin of the arrow is in the center of the stem cap // the arrow is contained in XY plane and has its main axis along the Y axis // used to render sidebar hints for position and scale - GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); + GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); // create a diamond with the given resolution // the origin of the diamond is in its center // the diamond is contained into a box with size [1, 1, 1] - GLModel::InitializationData diamond(int resolution); + GLModel::Geometry diamond(unsigned int resolution); + + // create a sphere with smooth normals + // the origin of the sphere is in its center + GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); + // create a cylinder with smooth normals + // the axis of the cylinder is the Z axis + // the origin of the cylinder is the center of its bottom cap face + GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); + // create a torus with smooth normals + // the axis of the torus is the Z axis + // the origin of the torus is in its center + GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index f6dfbe10f7..68f9d096eb 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -1,5 +1,10 @@ +///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLSelectionRectangle.hpp" #include "Camera.hpp" +#include "CameraUtils.hpp" #include "3DScene.hpp" #include "GLCanvas3D.hpp" #include "GUI_App.hpp" @@ -29,35 +34,19 @@ namespace GUI { m_end_corner = mouse_position; } - std::vector GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector& points) + std::vector GLSelectionRectangle::contains(const std::vector& points) const { std::vector out; - if (!is_dragging()) - return out; - - m_state = Off; - - const Camera& camera = wxGetApp().plater()->get_camera(); - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection= camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); - - // Convert our std::vector to Eigen dynamic matrix. - Eigen::Matrix pts(points.size(), 3); - for (size_t i=0; i(i, 0) = points[i]; - - // Get the projections. - Eigen::Matrix projections; - igl::project(pts, modelview, projection, viewport, projections); - // bounding box created from the rectangle corners - will take care of order of the corners - BoundingBox rectangle(Points{ Point(m_start_corner.cast()), Point(m_end_corner.cast()) }); + const BoundingBox rectangle(Points{ Point(m_start_corner.cast()), Point(m_end_corner.cast()) }); // Iterate over all points and determine whether they're in the rectangle. - for (int i = 0; iget_camera(); + Points points_2d = CameraUtils::project(camera, points); + unsigned int size = static_cast(points.size()); + for (unsigned int i = 0; i< size; ++i) + if (rectangle.contains(points_2d[i])) out.push_back(i); return out; @@ -69,59 +58,70 @@ namespace GUI { m_state = Off; } - void GLSelectionRectangle::render(const GLCanvas3D& canvas) const + void GLSelectionRectangle::render(const GLCanvas3D& canvas) { if (!is_dragging()) return; - const Camera& camera = wxGetApp().plater()->get_camera(); - float inv_zoom = (float)camera.get_inv_zoom(); - - Size cnv_size = canvas.get_canvas_size(); - float cnv_half_width = 0.5f * (float)cnv_size.get_width(); - float cnv_half_height = 0.5f * (float)cnv_size.get_height(); - if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) + const Size cnv_size = canvas.get_canvas_size(); + const float cnv_width = (float)cnv_size.get_width(); + const float cnv_height = (float)cnv_size.get_height(); + if (cnv_width == 0.0f || cnv_height == 0.0f) return; - Vec2d start(m_start_corner(0) - cnv_half_width, cnv_half_height - m_start_corner(1)); - Vec2d end(m_end_corner(0) - cnv_half_width, cnv_half_height - m_end_corner(1)); - - float left = (float)std::min(start(0), end(0)) * inv_zoom; - float top = (float)std::max(start(1), end(1)) * inv_zoom; - float right = (float)std::max(start(0), end(0)) * inv_zoom; - float bottom = (float)std::min(start(1), end(1)) * inv_zoom; - + const float cnv_inv_width = 1.0f / cnv_width; + const float cnv_inv_height = 1.0f / cnv_height; + const float left = 2.0f * (get_left() * cnv_inv_width - 0.5f); + const float right = 2.0f * (get_right() * cnv_inv_width - 0.5f); + const float top = -2.0f * (get_top() * cnv_inv_height - 0.5f); + const float bottom = -2.0f * (get_bottom() * cnv_inv_height - 0.5f); + glsafe(::glLineWidth(1.5f)); - float color[3]; - color[0] = 0.00f; - color[1] = 1.00f; - color[2] = 0.38f; - glsafe(::glColor3fv(color)); glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - // ensure that the rectangle is renderered inside the frustrum - glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); - // ensure that the overlay fits the frustrum near z plane - double gui_scale = camera.get_gui_scale(); - glsafe(::glScaled(gui_scale, gui_scale, 1.0)); - glsafe(::glPushAttrib(GL_ENABLE_BIT)); glsafe(::glLineStipple(4, 0xAAAA)); glsafe(::glEnable(GL_LINE_STIPPLE)); - ::glBegin(GL_LINE_LOOP); - ::glVertex2f((GLfloat)left, (GLfloat)bottom); - ::glVertex2f((GLfloat)right, (GLfloat)bottom); - ::glVertex2f((GLfloat)right, (GLfloat)top); - ::glVertex2f((GLfloat)left, (GLfloat)top); - glsafe(::glEnd()); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + if (!m_rectangle.is_initialized() || !m_old_start_corner.isApprox(m_start_corner) || !m_old_end_corner.isApprox(m_end_corner)) { + m_old_start_corner = m_start_corner; + m_old_end_corner = m_end_corner; + m_rectangle.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P2 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(4); + + // vertices + init_data.add_vertex(Vec2f(left, bottom)); + init_data.add_vertex(Vec2f(right, bottom)); + init_data.add_vertex(Vec2f(right, top)); + init_data.add_vertex(Vec2f(left, top)); + + // indices + init_data.add_index(0); + init_data.add_index(1); + init_data.add_index(2); + init_data.add_index(3); + + m_rectangle.init_from(std::move(init_data)); + } + + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + m_rectangle.set_color({0.0f, 1.0f, 0.38f, 1.0f}); + m_rectangle.render(); + shader->stop_using(); + } glsafe(::glPopAttrib()); - - glsafe(::glPopMatrix()); } } // namespace GUI diff --git a/src/slic3r/GUI/GLSelectionRectangle.hpp b/src/slic3r/GUI/GLSelectionRectangle.hpp index d9869771ef..8d87e18e46 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.hpp +++ b/src/slic3r/GUI/GLSelectionRectangle.hpp @@ -1,7 +1,12 @@ +///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLSelectionRectangle_hpp_ #define slic3r_GLSelectionRectangle_hpp_ #include "libslic3r/Point.hpp" +#include "GLModel.hpp" namespace Slic3r { namespace GUI { @@ -24,28 +29,31 @@ public: void dragging(const Vec2d& mouse_position); // Given a vector of points in world coordinates, the function returns indices of those - // that are in the rectangle. It then disables the rectangle. - std::vector stop_dragging(const GLCanvas3D& canvas, const std::vector& points); + // that are in the rectangle. + std::vector contains(const std::vector& points) const; // Disables the rectangle. void stop_dragging(); - void render(const GLCanvas3D& canvas) const; + void render(const GLCanvas3D& canvas); bool is_dragging() const { return m_state != Off; } EState get_state() const { return m_state; } - float get_width() const { return std::abs(m_start_corner(0) - m_end_corner(0)); } - float get_height() const { return std::abs(m_start_corner(1) - m_end_corner(1)); } - float get_left() const { return std::min(m_start_corner(0), m_end_corner(0)); } - float get_right() const { return std::max(m_start_corner(0), m_end_corner(0)); } - float get_top() const { return std::max(m_start_corner(1), m_end_corner(1)); } - float get_bottom() const { return std::min(m_start_corner(1), m_end_corner(1)); } + float get_width() const { return std::abs(m_start_corner.x() - m_end_corner.x()); } + float get_height() const { return std::abs(m_start_corner.y() - m_end_corner.y()); } + float get_left() const { return std::min(m_start_corner.x(), m_end_corner.x()); } + float get_right() const { return std::max(m_start_corner.x(), m_end_corner.x()); } + float get_top() const { return std::max(m_start_corner.y(), m_end_corner.y()); } + float get_bottom() const { return std::min(m_start_corner.y(), m_end_corner.y()); } private: - EState m_state = Off; - Vec2d m_start_corner; - Vec2d m_end_corner; + EState m_state{ Off }; + Vec2d m_start_corner{ Vec2d::Zero() }; + Vec2d m_end_corner{ Vec2d::Zero() }; + GLModel m_rectangle; + Vec2d m_old_start_corner{ Vec2d::Zero() }; + Vec2d m_old_end_corner{ Vec2d::Zero() }; }; diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 9c1e936525..5146f03fc6 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -4,6 +4,7 @@ #include "3DScene.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/format.hpp" +#include "libslic3r/Color.hpp" #include #include @@ -121,8 +122,7 @@ bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSourc for (size_t i = 0; i < static_cast(EShaderType::Count); ++i) { const std::string& source = sources[i]; - if (!source.empty()) - { + if (!source.empty()) { EShaderType type = static_cast(i); auto [result, id] = create_shader(type); if (result) @@ -206,154 +206,147 @@ void GLShaderProgram::stop_using() const glsafe(::glUseProgram(0)); } -bool GLShaderProgram::set_uniform(const char* name, int value) const +void GLShaderProgram::set_uniform(int id, int value) const { - int id = get_uniform_location(name); - if (id >= 0) { - glsafe(::glUniform1i(id, static_cast(value))); - return true; - } - return false; + if (id >= 0) + glsafe(::glUniform1i(id, value)); } -bool GLShaderProgram::set_uniform(const char* name, bool value) const +void GLShaderProgram::set_uniform(int id, bool value) const { - return set_uniform(name, value ? 1 : 0); + set_uniform(id, value ? 1 : 0); } -bool GLShaderProgram::set_uniform(const char* name, float value) const +void GLShaderProgram::set_uniform(int id, float value) const { - int id = get_uniform_location(name); - if (id >= 0) { - glsafe(::glUniform1f(id, static_cast(value))); - return true; - } - return false; + if (id >= 0) + glsafe(::glUniform1f(id, value)); } -bool GLShaderProgram::set_uniform(const char* name, double value) const +void GLShaderProgram::set_uniform(int id, double value) const { - return set_uniform(name, static_cast(value)); + set_uniform(id, static_cast(value)); } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform2iv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform3iv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform4iv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform2fv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform3fv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const std::array& value) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniform4fv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const +void GLShaderProgram::set_uniform(int id, const std::array& value) const { - if (size == 1) - return set_uniform(name, value[0]); - else if (size < 5) { - int id = get_uniform_location(name); - if (id >= 0) { - if (size == 2) - glsafe(::glUniform2fv(id, 1, static_cast(value))); - else if (size == 3) - glsafe(::glUniform3fv(id, 1, static_cast(value))); - else - glsafe(::glUniform4fv(id, 1, static_cast(value))); - - return true; - } - } - return false; + const std::array f_value = { float(value[0]), float(value[1]), float(value[2]), float(value[3]) }; + set_uniform(id, f_value); } -bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const +void GLShaderProgram::set_uniform(int id, const float* value, size_t size) const { - int id = get_uniform_location(name); if (id >= 0) { + if (size == 1) + set_uniform(id, value[0]); + else if (size == 2) + glsafe(::glUniform2fv(id, 1, static_cast(value))); + else if (size == 3) + glsafe(::glUniform3fv(id, 1, static_cast(value))); + else if (size == 4) + glsafe(::glUniform4fv(id, 1, static_cast(value))); + } +} + +void GLShaderProgram::set_uniform(int id, const Transform3f& value) const +{ + if (id >= 0) glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast(value.matrix().data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const +void GLShaderProgram::set_uniform(int id, const Transform3d& value) const { - return set_uniform(name, value.cast()); + set_uniform(id, value.cast()); } -bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const +void GLShaderProgram::set_uniform(int id, const Matrix3f& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + if (id >= 0) glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const +void GLShaderProgram::set_uniform(int id, const Matrix3d& value) const { - int id = get_uniform_location(name); - if (id >= 0) { + set_uniform(id, (Matrix3f)value.cast()); +} + +void GLShaderProgram::set_uniform(int id, const Matrix4f& value) const +{ + if (id >= 0) + glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast(value.data()))); +} + +void GLShaderProgram::set_uniform(int id, const Matrix4d& value) const +{ + set_uniform(id, (Matrix4f)value.cast()); +} + +void GLShaderProgram::set_uniform(int id, const Vec2f& value) const +{ + if (id >= 0) + glsafe(::glUniform2fv(id, 1, static_cast(value.data()))); +} + +void GLShaderProgram::set_uniform(int id, const Vec2d& value) const +{ + set_uniform(id, static_cast(value.cast())); +} + +void GLShaderProgram::set_uniform(int id, const Vec3f& value) const +{ + if (id >= 0) glsafe(::glUniform3fv(id, 1, static_cast(value.data()))); - return true; - } - return false; } -bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const +void GLShaderProgram::set_uniform(int id, const Vec3d& value) const { - return set_uniform(name, static_cast(value.cast())); + set_uniform(id, static_cast(value.cast())); +} + +void GLShaderProgram::set_uniform(int id, const ColorRGB& value) const +{ + set_uniform(id, value.data(), 3); +} + +void GLShaderProgram::set_uniform(int id, const ColorRGBA& value) const +{ + set_uniform(id, value.data(), 4); } int GLShaderProgram::get_attrib_location(const char* name) const diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index d7b92000df..935daaaeaa 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -9,6 +9,9 @@ namespace Slic3r { +class ColorRGB; +class ColorRGBA; + class GLShaderProgram { public: @@ -44,22 +47,55 @@ public: void start_using() const; void stop_using() const; - bool set_uniform(const char* name, int value) const; - bool set_uniform(const char* name, bool value) const; - bool set_uniform(const char* name, float value) const; - bool set_uniform(const char* name, double value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const std::array& value) const; - bool set_uniform(const char* name, const float* value, size_t size) const; - bool set_uniform(const char* name, const Transform3f& value) const; - bool set_uniform(const char* name, const Transform3d& value) const; - bool set_uniform(const char* name, const Matrix3f& value) const; - bool set_uniform(const char* name, const Vec3f& value) const; - bool set_uniform(const char* name, const Vec3d& value) const; + void set_uniform(const char* name, int value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, bool value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, float value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, double value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const std::array& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const float* value, size_t size) const { set_uniform(get_uniform_location(name), value, size); } + void set_uniform(const char* name, const Transform3f& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Transform3d& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Matrix3f& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Matrix3d& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Matrix4f& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Matrix4d& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Vec2f& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Vec2d& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Vec3f& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const Vec3d& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const ColorRGB& value) const { set_uniform(get_uniform_location(name), value); } + void set_uniform(const char* name, const ColorRGBA& value) const { set_uniform(get_uniform_location(name), value); } + + void set_uniform(int id, int value) const; + void set_uniform(int id, bool value) const; + void set_uniform(int id, float value) const; + void set_uniform(int id, double value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const std::array& value) const; + void set_uniform(int id, const float* value, size_t size) const; + void set_uniform(int id, const Transform3f& value) const; + void set_uniform(int id, const Transform3d& value) const; + void set_uniform(int id, const Matrix3f& value) const; + void set_uniform(int id, const Matrix3d& value) const; + void set_uniform(int id, const Matrix4f& value) const; + void set_uniform(int id, const Matrix4d& value) const; + void set_uniform(int id, const Vec2f& value) const; + void set_uniform(int id, const Vec2d& value) const; + void set_uniform(int id, const Vec3f& value) const; + void set_uniform(int id, const Vec3d& value) const; + void set_uniform(int id, const ColorRGB& value) const; + void set_uniform(int id, const ColorRGBA& value) const; // returns -1 if not found int get_attrib_location(const char* name) const; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 107fcb5627..d525ce272e 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -33,61 +33,48 @@ std::pair GLShadersManager::init() bool valid = true; + const std::string prefix = GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 1) ? "140/" : "110/"; + // imgui shader + valid &= append_shader("imgui", { prefix + "imgui.vs", prefix + "imgui.fs" }); + // basic shader, used to render all what was previously rendered using the immediate mode + valid &= append_shader("flat", { prefix + "flat.vs", prefix + "flat.fs" }); + // basic shader with plane clipping, used to render volumes in picking pass + valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" }); + // basic shader for textures, used to render textures + valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" }); + // used to render 3D scene background + valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" }); // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview - valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); + valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" }); //used to render thumbnail - valid &= append_shader("thumbnail", { "thumbnail.vs", "thumbnail.fs" }); - // used to render first layer for calibration - valid &= append_shader("cali", { "cali.vs", "cali.fs"}); + valid &= append_shader("thumbnail", { prefix + "thumbnail.vs", prefix + "thumbnail.fs"}); // used to render printbed - valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); + valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" }); // used to render options in gcode preview - if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) - valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); - // used to render extrusion and travel paths as lines in gcode preview - valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); + if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) { + valid &= append_shader("gouraud_light_instanced", { prefix + "gouraud_light_instanced.vs", prefix + "gouraud_light_instanced.fs" }); + } // used to render objects in 3d editor - //if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0)) { - if (0) { - valid &= append_shader("gouraud", { "gouraud_130.vs", "gouraud_130.fs" } -#if ENABLE_ENVIRONMENT_MAP - , { "ENABLE_ENVIRONMENT_MAP"sv } -#endif // ENABLE_ENVIRONMENT_MAP - ); - } - else { - valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" } + valid &= append_shader("gouraud", { prefix + "gouraud.vs", prefix + "gouraud.fs" } #if ENABLE_ENVIRONMENT_MAP , { "ENABLE_ENVIRONMENT_MAP"sv } #endif // ENABLE_ENVIRONMENT_MAP ); - } // used to render variable layers heights in 3d editor - valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); + valid &= append_shader("variable_layer_height", { prefix + "variable_layer_height.vs", prefix + "variable_layer_height.fs" }); // used to render highlight contour around selected triangles inside the multi-material gizmo - valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" }); + valid &= append_shader("mm_contour", { prefix + "mm_contour.vs", prefix + "mm_contour.fs" }); // Used to render painted triangles inside the multi-material gizmo. Triangle normals are computed inside fragment shader. // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction. // Because of this, objects had darker colors inside the multi-material gizmo. // Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU. // Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed // triangle normals inside fragment shader have the right direction. - if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12) { - //if (GUI::wxGetApp().plater() && GUI::wxGetApp().plater()->is_wireframe_enabled()) - // valid &= append_shader("mm_gouraud", {"mm_gouraud_wireframe.vs", "mm_gouraud_wireframe.fs"}, {"FLIP_TRIANGLE_NORMALS"sv}); - //else - valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv}); - } - else { - //if (GUI::wxGetApp().plater() && GUI::wxGetApp().plater()->is_wireframe_enabled()) - // valid &= append_shader("mm_gouraud", {"mm_gouraud_wireframe.vs", "mm_gouraud_wireframe.fs"}); - //else - valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}); - } - - //BBS: add shader for outline - valid &= append_shader("outline", { "outline.vs", "outline.fs" }); + if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12) + valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv }); + else + valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }); return { valid, error }; } diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index bcf1ff07c0..c1137487e1 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ //BBS:add i18n #include "I18N.hpp" //BBS: add fstream for debug output @@ -8,6 +12,8 @@ #include "3DScene.hpp" #include "OpenGLManager.hpp" +#include "GUI_App.hpp" +#include "GLModel.hpp" #include @@ -125,11 +131,7 @@ void GLTexture::Compressor::compress() GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; GLTexture::GLTexture() - : m_id(0) - , m_width(0) - , m_height(0) - , m_source("") - , m_compressor(*this) + : m_compressor(*this) { } @@ -418,13 +420,13 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vectorstart_using(); + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + model.render(); + shader->stop_using(); + } glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); @@ -677,9 +699,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, glsafe(::glDisable(GL_BLEND)); } +static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h) +{ + auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; }; + auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; + + int new_w = std::max(w, h); + if (!is_power_of_two(new_w)) + new_w = upper_power_of_two(new_w); + + while (new_w > max_size_px) { + new_w /= 2; + } + + const int new_h = new_w; + const bool ret = (new_w != w || new_h != h); + w = new_w; + h = new_h; + return ret; +} + bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy) { - bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc; + const bool compression_enabled = (compression_type != None) && OpenGLManager::are_compressed_textures_supported(); // Load a PNG with an alpha channel. wxImage image; @@ -693,6 +735,11 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo bool requires_rescale = false; + if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures()) { + if (to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), OpenGLManager::get_gl_info().get_max_tex_size(), m_width, m_height)) + requires_rescale = true; + } + if (compression_enabled && compression_type == MultiThreaded) { // the stb_dxt compression library seems to like only texture sizes which are a multiple of 4 int width_rem = m_width % 4; @@ -819,7 +866,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo m_source = filename; - if (compression_enabled && compression_type == MultiThreaded) + if (compression_type == MultiThreaded) // start asynchronous compression m_compressor.start_compressing(); @@ -828,7 +875,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) { - bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc; + const bool compression_enabled = compress && OpenGLManager::are_compressed_textures_supported(); NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); if (image == nullptr) { @@ -836,11 +883,17 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo return false; } - float scale = (float)max_size_px / std::max(image->width, image->height); + const float scale = (float)max_size_px / std::max(image->width, image->height); m_width = (int)(scale * image->width); m_height = (int)(scale * image->height); + if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures()) + to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), max_size_px, m_width, m_height); + + float scale_w = (float)m_width / image->width; + float scale_h = (float)m_height / image->height; + if (compression_enabled) { // the stb_dxt compression library seems to like only texture sizes which are a multiple of 4 int width_rem = m_width % 4; @@ -853,7 +906,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo m_height += (4 - height_rem); } - int n_pixels = m_width * m_height; + const int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); @@ -870,7 +923,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo // creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps std::vector data(n_pixels * 4, 0); - nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4); + nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), m_width, m_height, m_width * 4); // sends data to gpu glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); @@ -892,7 +945,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo else glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - if (use_mipmaps && OpenGLManager::use_manually_generated_mipmaps()) { + if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards int lod_w = m_width; int lod_h = m_height; @@ -902,11 +955,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo lod_w = std::max(lod_w / 2, 1); lod_h = std::max(lod_h / 2, 1); - scale /= 2.0f; + scale_w /= 2.0f; + scale_h /= 2.0f; data.resize(lod_w * lod_h * 4); - nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4); + nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), lod_w, lod_h, lod_w * 4); if (compression_enabled) { // initializes the texture on GPU glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); @@ -921,9 +975,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); } - } else if (use_mipmaps && !OpenGLManager::use_manually_generated_mipmaps()) { - glGenerateMipmap(GL_TEXTURE_2D); - } else { + } + else { glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); } diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index d898a10bdf..9af4f8a872 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -64,8 +64,8 @@ namespace GUI { struct UV { - float u; - float v; + float u{ 0.0f }; + float v{ 0.0f }; }; struct Quad_UVs @@ -79,9 +79,9 @@ namespace GUI { static Quad_UVs FullTextureUVs; protected: - unsigned int m_id; - int m_width; - int m_height; + unsigned int m_id{ 0 }; + int m_width{ 0 }; + int m_height{ 0 }; std::string m_source; Compressor m_compressor; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 6a78edd907..8e99bb1bc2 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/Point.hpp" #include "libslic3r/libslic3r.h" @@ -281,21 +285,13 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) return res; } -bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture) +bool GLToolbar::init_arrow(const std::string& filename) { - if (m_arrow_texture.texture.get_id() != 0) + if (m_arrow_texture.get_id() != 0) return true; - std::string path = resources_dir() + "/images/"; - bool res = false; - - if (!arrow_texture.filename.empty()) { - res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); - } - if (res) - m_arrow_texture.metadata = arrow_texture; - - return res; + const std::string path = resources_dir() + "/images/"; + return (!filename.empty()) ? m_arrow_texture.load_from_svg_file(path + filename, false, false, false, 1000) : false; } GLToolbar::Layout::EType GLToolbar::get_layout_type() const @@ -551,18 +547,16 @@ void GLToolbar::render(const GLCanvas3D& parent,GLToolbarItem::EType type) { default: case Layout::Horizontal: { render_horizontal(parent,type); break; } - case Layout::Vertical: { render_vertical(parent); break; } + case Layout::Vertical: { render_vertical(parent); break; } } } - - bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { if (!m_enabled) return false; - Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); + const Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); bool processed = false; // mouse anywhere @@ -610,7 +604,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) return false; } - int item_id = contains_mouse(mouse_pos, parent); + const int item_id = contains_mouse(mouse_pos, parent); if (item_id != -1) { // mouse inside toolbar if (evt.LeftDown() || evt.LeftDClick()) { @@ -757,16 +751,12 @@ int GLToolbar::get_visible_items_cnt() const void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover) { - if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) - { - if ((0 <= item_id) && (item_id < (int)m_items.size())) - { + if (m_pressed_toggable_id == -1 || m_pressed_toggable_id == item_id) { + if (0 <= item_id && item_id < (int)m_items.size()) { GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered())) - { - if (((type == GLToolbarItem::Right) && item->is_right_toggable()) || - ((type == GLToolbarItem::Left) && item->is_left_toggable())) - { + if (item != nullptr && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered())) { + if ((type == GLToolbarItem::Right && item->is_right_toggable()) || + (type == GLToolbarItem::Left && item->is_left_toggable())) { GLToolbarItem::EState state = item->get_state(); if (state == GLToolbarItem::Hover) item->set_state(GLToolbarItem::HoverPressed); @@ -784,12 +774,11 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas switch (type) { default: - case GLToolbarItem::Left: { item->do_left_action(); break; } + case GLToolbarItem::Left: { item->do_left_action(); break; } case GLToolbarItem::Right: { item->do_right_action(); break; } } } - else - { + else { if (m_type == Radio) select_item(item->get_name()); else @@ -804,8 +793,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas case GLToolbarItem::Right: { item->do_right_action(); break; } } - if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) - { + if (m_type == Normal && item->get_state() != GLToolbarItem::Disabled) { // the item may get disabled during the action, if not, set it back to normal state item->set_state(GLToolbarItem::Normal); parent.render(); @@ -825,55 +813,51 @@ void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) { default: case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } + case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } } } void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) { - // NB: mouse_pos is already scaled appropriately + const Size cnv_size = parent.get_canvas_size(); + const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y())); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; + const float icons_size = m_layout.icons_size * m_layout.scale; + const float separator_size = m_layout.separator_size * m_layout.scale; + const float gap_size = m_layout.gap_size * m_layout.scale; + const float border = m_layout.border * m_layout.scale; - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + const float separator_stride = separator_size + gap_size; + const float icon_stride = icons_size + gap_size; - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; + float left = m_layout.left + border; + float top = m_layout.top - border; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (GLToolbarItem* item : m_items) - { + for (GLToolbarItem* item : m_items) { if (!item->is_visible()) continue; if (item->is_separator()) left += separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; + else { + float right = left + icons_size; + const float bottom = top - icons_size; //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); - GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); + right += icons_size * item->get_extra_size_ratio(); + + const GLToolbarItem::EState state = item->get_state(); + bool inside = (left <= (float)scaled_mouse_pos.x()) && + ((float)scaled_mouse_pos.x() <= right) && + (bottom <= (float)scaled_mouse_pos.y()) && + ((float)scaled_mouse_pos.y() <= top); switch (state) { case GLToolbarItem::Normal: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::Hover); parent.set_as_dirty(); } @@ -882,8 +866,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } case GLToolbarItem::Hover: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Normal); parent.set_as_dirty(); } @@ -892,8 +875,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } case GLToolbarItem::Pressed: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::HoverPressed); parent.set_as_dirty(); } @@ -902,8 +884,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } case GLToolbarItem::HoverPressed: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Pressed); parent.set_as_dirty(); } @@ -912,8 +893,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } case GLToolbarItem::Disabled: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::HoverDisabled); parent.set_as_dirty(); } @@ -922,8 +902,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } case GLToolbarItem::HoverDisabled: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Disabled); parent.set_as_dirty(); } @@ -939,59 +918,55 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D left += icon_stride; //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) - left += scaled_icons_size * item->get_extra_size_ratio(); + left += icons_size * item->get_extra_size_ratio(); } } } void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) { - // NB: mouse_pos is already scaled appropriately + const Size cnv_size = parent.get_canvas_size(); + const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y())); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; + const float icons_size = m_layout.icons_size * m_layout.scale; + const float separator_size = m_layout.separator_size * m_layout.scale; + const float gap_size = m_layout.gap_size * m_layout.scale; + const float border = m_layout.border * m_layout.scale; - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + const float separator_stride = separator_size + gap_size; + const float icon_stride = icons_size + gap_size; - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; + float left = m_layout.left + border; + float top = m_layout.top - border; - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (GLToolbarItem* item : m_items) - { + for (GLToolbarItem* item : m_items) { if (!item->is_visible()) continue; if (item->is_separator()) top -= separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; + else { + float right = left + icons_size; + const float bottom = top - icons_size; if (item->is_action_with_text_image()) - right += m_layout.text_size * factor; + right += m_layout.text_size * m_layout.scale; //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); + right += icons_size * item->get_extra_size_ratio(); GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); + const bool inside = (left <= (float)scaled_mouse_pos.x()) && + ((float)scaled_mouse_pos.x() <= right) && + (bottom <= (float)scaled_mouse_pos.y()) && + ((float)scaled_mouse_pos.y() <= top); switch (state) { case GLToolbarItem::Normal: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::Hover); parent.set_as_dirty(); } @@ -1000,8 +975,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } case GLToolbarItem::Hover: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Normal); parent.set_as_dirty(); } @@ -1010,8 +984,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } case GLToolbarItem::Pressed: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::HoverPressed); parent.set_as_dirty(); } @@ -1020,8 +993,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } case GLToolbarItem::HoverPressed: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Pressed); parent.set_as_dirty(); } @@ -1030,8 +1002,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } case GLToolbarItem::Disabled: { - if (inside) - { + if (inside) { item->set_state(GLToolbarItem::HoverDisabled); parent.set_as_dirty(); } @@ -1040,8 +1011,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } case GLToolbarItem::HoverDisabled: { - if (!inside) - { + if (!inside) { item->set_state(GLToolbarItem::Disabled); parent.set_as_dirty(); } @@ -1083,77 +1053,78 @@ int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) { default: case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } - case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } + case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } } } int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const { - // NB: mouse_pos is already scaled appropriately + const Size cnv_size = parent.get_canvas_size(); + const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y())); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; + const float icons_size = m_layout.icons_size * m_layout.scale; + const float separator_size = m_layout.separator_size * m_layout.scale; + const float gap_size = m_layout.gap_size * m_layout.scale; + const float border = m_layout.border * m_layout.scale; - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + float left = m_layout.left + border; + const float top = m_layout.top - border; - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - - for (size_t id=0; idis_visible()) continue; - if (item->is_separator()) - { - float right = left + scaled_separator_size; - float bottom = top - scaled_icons_size; + if (item->is_separator()) { + float right = left + separator_size; + const float bottom = top - icons_size; // mouse inside the separator - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return id; left = right; - right += scaled_gap_size; + right += gap_size; - if (id < m_items.size() - 1) - { + if (id < m_items.size() - 1) { // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return -2; } left = right; } - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; + else { + float right = left + icons_size; + const float bottom = top - icons_size; //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); + right += icons_size * item->get_extra_size_ratio(); // mouse inside the icon - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return id; left = right; - right += scaled_gap_size; + right += gap_size; - if (id < m_items.size() - 1) - { + if (id < m_items.size() - 1) { // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return -2; } @@ -1166,73 +1137,75 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const { - // NB: mouse_pos is already scaled appropriately + const Size cnv_size = parent.get_canvas_size(); + const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y())); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; + const float icons_size = m_layout.icons_size * m_layout.scale; + const float separator_size = m_layout.separator_size * m_layout.scale; + const float gap_size = m_layout.gap_size * m_layout.scale; + const float border = m_layout.border * m_layout.scale; - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); + const float left = m_layout.left + border; + float top = m_layout.top - border; - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (size_t id=0; idis_visible()) continue; - if (item->is_separator()) - { - float right = left + scaled_icons_size; - float bottom = top - scaled_separator_size; + if (item->is_separator()) { + const float right = left + icons_size; + float bottom = top - separator_size; // mouse inside the separator - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return id; top = bottom; - bottom -= scaled_gap_size; + bottom -= gap_size; - if (id < m_items.size() - 1) - { + if (id < m_items.size() - 1) { // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return -2; } top = bottom; } - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; + else { + float right = left + icons_size; + float bottom = top - icons_size; if (item->is_action_with_text_image()) - right += m_layout.text_size * factor; + right += m_layout.text_size * m_layout.scale; //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); + right += icons_size * item->get_extra_size_ratio(); // mouse inside the icon - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return id; top = bottom; - bottom -= scaled_gap_size; + bottom -= gap_size; - if (id < m_items.size() - 1) - { + if (id < m_items.size() - 1) { // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + if (left <= (float)scaled_mouse_pos.x() && + (float)scaled_mouse_pos.x() <= right && + bottom <= (float)scaled_mouse_pos.y() && + (float)scaled_mouse_pos.y() <= top) return -2; } @@ -1243,33 +1216,32 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& return -1; } -void GLToolbar::render_background(float left, float top, float right, float bottom, float border) const +void GLToolbar::render_background(float left, float top, float right, float bottom, float border_w, float border_h) const { - unsigned int tex_id = m_background_texture.texture.get_id(); - float tex_width = (float)m_background_texture.texture.get_width(); - float tex_height = (float)m_background_texture.texture.get_height(); - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) - { - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + const unsigned int tex_id = m_background_texture.texture.get_id(); + const float tex_width = (float)m_background_texture.texture.get_width(); + const float tex_height = (float)m_background_texture.texture.get_height(); + if (tex_id != 0 && tex_width > 0.0f && tex_height > 0.0f) { + const float inv_tex_width = 1.0f / tex_width; + const float inv_tex_height = 1.0f / tex_height; - float internal_left = left + border; - float internal_right = right - border; - float internal_top = top - border; - float internal_bottom = bottom + border; + const float internal_left = left + border_w; + const float internal_right = right - border_w; + const float internal_top = top - border_h; + const float internal_bottom = bottom + border_w; - float left_uv = 0.0f; - float right_uv = 1.0f; - float top_uv = 1.0f; - float bottom_uv = 0.0f; + const float left_uv = 0.0f; + const float right_uv = 1.0f; + const float top_uv = 1.0f; + const float bottom_uv = 0.0f; - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; + const float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; + const float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; + const float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; + const float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; // top-left corner - if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Top)) + if (m_layout.horizontal_orientation == Layout::HO_Left || m_layout.vertical_orientation == Layout::VO_Top) GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); else GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } }); @@ -1281,7 +1253,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); // top-right corner - if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Top)) + if (m_layout.horizontal_orientation == Layout::HO_Right || m_layout.vertical_orientation == Layout::VO_Top) GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); else GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); @@ -1302,7 +1274,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); // bottom-left corner - if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Bottom)) + if (m_layout.horizontal_orientation == Layout::HO_Left || m_layout.vertical_orientation == Layout::VO_Bottom) GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); else GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } }); @@ -1314,7 +1286,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); // bottom-right corner - if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Bottom)) + if (m_layout.horizontal_orientation == Layout::HO_Right || m_layout.vertical_orientation == Layout::VO_Bottom) GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); else GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); @@ -1324,7 +1296,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item) { // arrow texture not initialized - if (m_arrow_texture.texture.get_id() == 0) + if (m_arrow_texture.get_id() == 0) return; float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); @@ -1363,67 +1335,71 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte top -= separator_stride; float right = left + scaled_icons_size; - unsigned int tex_id = m_arrow_texture.texture.get_id(); + const unsigned int tex_id = m_arrow_texture.get_id(); // arrow width and height - float arr_tex_width = (float)m_arrow_texture.texture.get_width(); - float arr_tex_height = (float)m_arrow_texture.texture.get_height(); - if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) { - float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f; - float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f; - + const float arr_tex_width = (float)m_arrow_texture.get_width(); + const float arr_tex_height = (float)m_arrow_texture.get_height(); + if (tex_id != 0 && arr_tex_width > 0.0f && arr_tex_height > 0.0f) { float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow float internal_right = right - border + scaled_icons_size * 1.5f; float internal_top = top - border; // bottom is not moving and should be calculated from arrow texture sides ratio - float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); + float arrow_sides_ratio = (float)m_arrow_texture.get_height() / (float)m_arrow_texture.get_width(); float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ; - float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height; + const float left_uv = 0.0f; + const float right_uv = 1.0f; + const float top_uv = 1.0f; + const float bottom_uv = 0.0f; - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { left_uv, top_uv }, { right_uv, top_uv }, { right_uv, bottom_uv }, { left_uv, bottom_uv } }); } } void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type) { - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = inv_zoom * m_layout.scale; + const Size cnv_size = parent.get_canvas_size(); + const float cnv_w = (float)cnv_size.get_width(); + const float cnv_h = (float)cnv_size.get_height(); - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float scaled_width = get_width() * inv_zoom; - float scaled_height = get_height() * inv_zoom; + if (cnv_w == 0 || cnv_h == 0) + return; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; + const float inv_cnv_w = 1.0f / cnv_w; + const float inv_cnv_h = 1.0f / cnv_h; - float left = m_layout.left; - float top = m_layout.top; - float right = left + scaled_width; + const float icons_size_x = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_w; + const float icons_size_y = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_h; + const float separator_size = 2.0f * m_layout.separator_size * m_layout.scale * inv_cnv_w; + const float gap_size = 2.0f * m_layout.gap_size * m_layout.scale * inv_cnv_w; + const float border_w = 2.0f * m_layout.border * m_layout.scale * inv_cnv_w; + const float border_h = 2.0f * m_layout.border * m_layout.scale * inv_cnv_h; + const float width = 2.0f * get_width() * inv_cnv_w; + const float height = 2.0f * get_height() * inv_cnv_h; + + const float separator_stride = separator_size + gap_size; + const float icon_stride = icons_size_x + gap_size; + + float left = 2.0f * m_layout.left * inv_cnv_w; + float top = 2.0f * m_layout.top * inv_cnv_h; + float right = left + width; if (type == GLToolbarItem::SeparatorLine) - right = left + scaled_width * 0.5; - float bottom = top - scaled_height; + right = left + width * 0.5; + const float bottom = top - height; - render_background(left, top, right, bottom, scaled_border); + render_background(left, top, right, bottom, border_w, border_h); - left += scaled_border; - top -= scaled_border; + left += border_w; + top -= border_h; // renders icons - for (const GLToolbarItem* item : m_items) - { + for (const GLToolbarItem* item : m_items) { if (!item->is_visible()) continue; if (item->is_separator()) left += separator_stride; - else - { + else { //BBS GUI refactor item->render_left_pos = left; if (!item->is_action_with_text_image()) { @@ -1432,13 +1408,13 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType int tex_height = m_icons_texture.get_height(); if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) return; - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); + item->render(tex_id, left, left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); } //BBS: GUI refactor: GLToolbar if (item->is_action_with_text()) { - float scaled_text_size = item->get_extra_size_ratio() * scaled_icons_size; - item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_size, top - scaled_icons_size, top); + float scaled_text_size = item->get_extra_size_ratio() * icons_size_x; + item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_size, top - icons_size_y, top); left += scaled_text_size; } left += icon_stride; @@ -1448,28 +1424,37 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType void GLToolbar::render_vertical(const GLCanvas3D& parent) { - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = inv_zoom * m_layout.scale; + const Size cnv_size = parent.get_canvas_size(); + const float cnv_w = (float)cnv_size.get_width(); + const float cnv_h = (float)cnv_size.get_height(); - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float scaled_width = get_width() * inv_zoom; - float scaled_height = get_height() * inv_zoom; + if (cnv_w == 0 || cnv_h == 0) + return; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; + const float inv_cnv_w = 1.0f / cnv_w; + const float inv_cnv_h = 1.0f / cnv_h; - float left = m_layout.left; - float top = m_layout.top; - float right = left + scaled_width; - float bottom = top - scaled_height; + const float icons_size_x = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_w; + const float icons_size_y = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_h; + const float separator_size = 2.0f * m_layout.separator_size * m_layout.scale * inv_cnv_h; + const float gap_size = 2.0f * m_layout.gap_size * m_layout.scale * inv_cnv_h; + const float border_w = 2.0f * m_layout.border * m_layout.scale * inv_cnv_w; + const float border_h = 2.0f * m_layout.border * m_layout.scale * inv_cnv_h; + const float width = 2.0f * get_width() * inv_cnv_w; + const float height = 2.0f * get_height() * inv_cnv_h; - render_background(left, top, right, bottom, scaled_border); + const float separator_stride = separator_size + gap_size; + const float icon_stride = icons_size_y + gap_size; - left += scaled_border; - top -= scaled_border; + float left = 2.0f * m_layout.left * inv_cnv_w; + float top = 2.0f * m_layout.top * inv_cnv_h; + const float right = left + width; + const float bottom = top - height; + + render_background(left, top, right, bottom, border_w, border_h); + + left += border_w; + top -= border_h; // renders icons for (const GLToolbarItem* item : m_items) { @@ -1482,10 +1467,10 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) unsigned int tex_id; int tex_width, tex_height; if (item->is_action_with_text_image()) { - float scaled_text_size = m_layout.text_size * factor; - float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; - float scaled_text_border = 2.5 * factor; - float scaled_text_height = scaled_icons_size / 2.0f; + float scaled_text_size = m_layout.text_size * m_layout.scale * inv_cnv_w; + float scaled_text_width = item->get_extra_size_ratio() * icons_size_x; + float scaled_text_border = 2.5 * m_layout.scale * inv_cnv_h; + float scaled_text_height = icons_size_y / 2.0f; item->render_text(left, left + scaled_text_size, top - scaled_text_border - scaled_text_height, top - scaled_text_border); float image_left = left + scaled_text_size; @@ -1494,7 +1479,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) tex_height = item->m_data.image_texture.get_height(); if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) return; - item->render_image(tex_id, image_left, image_left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); + item->render_image(tex_id, image_left, image_left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); } else { tex_id = m_icons_texture.get_id(); @@ -1502,14 +1487,14 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) tex_height = m_icons_texture.get_height(); if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) return; - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); + item->render(tex_id, left, left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); //BBS: GUI refactor: GLToolbar } if (item->is_action_with_text()) { - float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; - float scaled_text_height = scaled_icons_size; - item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_width, top - scaled_text_height, top); + float scaled_text_width = item->get_extra_size_ratio() * icons_size_x; + float scaled_text_height = icons_size_y; + item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_width, top - scaled_text_height, top); } top -= icon_stride; } diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 09da22d3cd..28ad69bb53 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Oleksandra Iushchenko @YuSanka, Vojtěch Král @vojtechkral, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLToolbar_hpp_ #define slic3r_GLToolbar_hpp_ @@ -327,7 +331,7 @@ private: mutable GLTexture m_images_texture; mutable bool m_images_texture_dirty; BackgroundTexture m_background_texture; - BackgroundTexture m_arrow_texture; + GLTexture m_arrow_texture; Layout m_layout; ItemsList m_items; @@ -354,7 +358,7 @@ public: bool init(const BackgroundTexture::Metadata& background_texture); - bool init_arrow(const BackgroundTexture::Metadata& arrow_texture); + bool init_arrow(const std::string& filename); Layout::EType get_layout_type() const; void set_layout_type(Layout::EType type); @@ -436,8 +440,8 @@ private: int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - void render_background(float left, float top, float right, float bottom, float border) const; - void render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type); + void render_background(float left, float top, float right, float bottom, float border_w, float border_h) const; + void render_horizontal(const GLCanvas3D &parent, GLToolbarItem::EType type); void render_vertical(const GLCanvas3D& parent); bool generate_icons_texture(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b9d3a8cd74..8e6c4f64a2 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -56,6 +56,7 @@ #include "libslic3r/Thread.hpp" #include "libslic3r/miniz_extension.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "GUI_Utils.hpp" @@ -3233,8 +3234,7 @@ void GUI_App::set_label_clr_modified(const wxColour& clr) if (m_color_label_modified == clr) return; m_color_label_modified = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); + const std::string str = encode_color(ColorRGB(clr.Red(), clr.Green(), clr.Blue())); app_config->save(); */ } @@ -3247,8 +3247,7 @@ void GUI_App::set_label_clr_sys(const wxColour& clr) if (m_color_label_sys == clr) return; m_color_label_sys = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); + const std::string str = encode_color(ColorRGB(clr.Red(), clr.Green(), clr.Blue())); app_config->save(); */ } @@ -5865,6 +5864,12 @@ Sidebar& GUI_App::sidebar() return plater_->sidebar(); } +GizmoObjectManipulation *GUI_App::obj_manipul() +{ + // If this method is called before plater_ has been initialized, return nullptr (to avoid a crash) + return (plater_ != nullptr) ? &plater_->get_view3D_canvas3D()->get_gizmos_manager().get_object_manipulation() : nullptr; +} + ObjectSettings* GUI_App::obj_settings() { return sidebar().obj_settings(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 2defd53a5d..7acf5e8f26 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -129,6 +129,7 @@ enum CameraMenuIDs { class Tab; class ConfigWizard; +class GizmoObjectManipulation; static wxString dots("...", wxConvUTF8); @@ -529,6 +530,7 @@ private: #endif /* __APPLE */ Sidebar& sidebar(); + GizmoObjectManipulation* obj_manipul(); ObjectSettings* obj_settings(); ObjectList* obj_list(); ObjectLayers* obj_layers(); diff --git a/src/slic3r/GUI/GUI_Colors.hpp b/src/slic3r/GUI/GUI_Colors.hpp index 32a0399181..0395e997c0 100644 --- a/src/slic3r/GUI/GUI_Colors.hpp +++ b/src/slic3r/GUI/GUI_Colors.hpp @@ -2,6 +2,7 @@ #define slic3r_GUI_Colors_hpp_ #include "imgui/imgui.h" +#include "libslic3r/Color.hpp" enum RenderCol_ { RenderCol_3D_Background = 0, @@ -38,13 +39,6 @@ public: static ImVec4 colors[RenderCol_Count]; }; const char* GetRenderColName(RenderCol idx); -inline std::array GLColor(ImVec4 color) { - return {color.x, color.y, color.z, color.w }; -} - -inline ImVec4 IMColor(std::array color) { - return ImVec4(color[0], color[1], color[2], color[3]); -} } diff --git a/src/slic3r/GUI/GUI_Geometry.cpp b/src/slic3r/GUI/GUI_Geometry.cpp new file mode 100644 index 0000000000..3cee023e12 --- /dev/null +++ b/src/slic3r/GUI/GUI_Geometry.cpp @@ -0,0 +1,13 @@ +///|/ Copyright (c) Prusa Research 2021 Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "libslic3r/libslic3r.h" +#include "GUI_Geometry.hpp" + +namespace Slic3r { +namespace GUI { + + +} // namespace Slic3r +} // namespace GUI diff --git a/src/slic3r/GUI/GUI_Geometry.hpp b/src/slic3r/GUI/GUI_Geometry.hpp new file mode 100644 index 0000000000..ed89af649c --- /dev/null +++ b/src/slic3r/GUI/GUI_Geometry.hpp @@ -0,0 +1,82 @@ +///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_GUI_Geometry_hpp_ +#define slic3r_GUI_Geometry_hpp_ + +namespace Slic3r { +namespace GUI { + +enum class ECoordinatesType : unsigned char +{ + World, + Instance, + Local +}; + +class TransformationType +{ +public: + enum Enum { + // Transforming in a world coordinate system + World = 0, + // Transforming in a instance coordinate system + Instance = 1, + // Transforming in a local coordinate system + Local = 2, + // Absolute transformations, allowed in local coordinate system only. + Absolute = 0, + // Relative transformations, allowed in both local and world coordinate system. + Relative = 4, + // For group selection, the transformation is performed as if the group made a single solid body. + Joint = 0, + // For group selection, the transformation is performed on each object independently. + Independent = 8, + + World_Relative_Joint = World | Relative | Joint, + World_Relative_Independent = World | Relative | Independent, + Instance_Absolute_Joint = Instance | Absolute | Joint, + Instance_Absolute_Independent = Instance | Absolute | Independent, + Instance_Relative_Joint = Instance | Relative | Joint, + Instance_Relative_Independent = Instance | Relative | Independent, + Local_Absolute_Joint = Local | Absolute | Joint, + Local_Absolute_Independent = Local | Absolute | Independent, + Local_Relative_Joint = Local | Relative | Joint, + Local_Relative_Independent = Local | Relative | Independent, + }; + + TransformationType() : m_value(World) {} + TransformationType(Enum value) : m_value(value) {} + TransformationType& operator=(Enum value) { m_value = value; return *this; } + + Enum operator()() const { return m_value; } + bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } + + void set_world() { this->remove(Instance); this->remove(Local); } + void set_instance() { this->remove(Local); this->add(Instance); } + void set_local() { this->remove(Instance); this->add(Local); } + void set_absolute() { this->remove(Relative); } + void set_relative() { this->add(Relative); } + void set_joint() { this->remove(Independent); } + void set_independent() { this->add(Independent); } + + bool world() const { return !this->has(Instance) && !this->has(Local); } + bool instance() const { return this->has(Instance); } + bool local() const { return this->has(Local); } + bool absolute() const { return !this->has(Relative); } + bool relative() const { return this->has(Relative); } + bool joint() const { return !this->has(Independent); } + bool independent() const { return this->has(Independent); } + +private: + void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); } + void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); } + + Enum m_value; +}; + +} // namespace Slic3r +} // namespace GUI + +#endif // slic3r_GUI_Geometry_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 0d2033646b..135e109a6a 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -22,8 +22,10 @@ namespace GUI ObjectLayers::ObjectLayers(wxWindow* parent) : OG_Settings(parent, true) { - m_grid_sizer = new wxFlexGridSizer(3, 0, wxGetApp().em_unit()); // "Min Z", "Max Z", "Layer height" & buttons sizer + m_grid_sizer = new wxFlexGridSizer(5, 0, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, unit & buttons sizer m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL); + m_grid_sizer->AddGrowableCol(1); + m_grid_sizer->AddGrowableCol(3); m_og->activate(); m_og->sizer->Clear(true); @@ -75,7 +77,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus auto head_text = new wxStaticText(m_parent, wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); head_text->SetBackgroundStyle(wxBG_STYLE_PAINT); head_text->SetFont(wxGetApp().normal_font()); - m_grid_sizer->Add(head_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + m_grid_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL); // Add control for the "Min Z" @@ -101,14 +103,12 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus select_editor(editor, is_last_edited_range); - auto sizer1 = new wxBoxSizer(wxHORIZONTAL); - sizer1->Add(editor); + m_grid_sizer->Add(editor, 1, wxEXPAND); + auto middle_text = new wxStaticText(m_parent, wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT); middle_text->SetFont(wxGetApp().normal_font()); - sizer1->Add(middle_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); - - m_grid_sizer->Add(sizer1); + m_grid_sizer->Add(middle_text, 0, wxALIGN_CENTER_VERTICAL); // Add control for the "Max Z" @@ -132,13 +132,13 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus }); //select_editor(editor, is_last_edited_range); + m_grid_sizer->Add(editor, 1, wxEXPAND); auto sizer2 = new wxBoxSizer(wxHORIZONTAL); - sizer2->Add(editor); auto unit_text = new wxStaticText(m_parent, wxID_ANY, _L("mm"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); unit_text->SetBackgroundStyle(wxBG_STYLE_PAINT); unit_text->SetFont(wxGetApp().normal_font()); - sizer2->Add(unit_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + sizer2->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); m_grid_sizer->Add(sizer2); @@ -335,7 +335,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent, m_type(type), m_set_focus_data(set_focus_data_fn), wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition, - wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER + wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER #ifdef _WIN32 | wxBORDER_SIMPLE #endif @@ -444,7 +444,7 @@ coordf_t LayerRangeEditor::get_value() void LayerRangeEditor::msw_rescale() { - SetMinSize(wxSize(8 * wxGetApp().em_unit(), wxDefaultCoord)); + SetMinSize(wxSize(wxGetApp().em_unit(), wxDefaultCoord)); } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 90f1a04060..5a50df455c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1,3 +1,9 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, David Kocík @kocikdav, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2021 Mathias Rasmussen +///|/ Copyright (c) 2020 rongith +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "libslic3r/PresetBundle.hpp" #include "GUI_ObjectList.hpp" @@ -304,6 +310,7 @@ ObjectList::ObjectList(wxWindow* parent) : ObjectList::~ObjectList() { + delete m_objects_model; } void ObjectList::set_min_height() @@ -1949,7 +1956,7 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo const BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx); // First (any) GLVolume of the selected instance. They all share the same instance matrix. - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* v = selection.get_first_volume(); const Geometry::Transformation inst_transform = v->get_instance_transformation(); const Transform3d inv_inst_transform = inst_transform.get_matrix(true).inverse(); const Vec3d instance_offset = v->get_instance_offset(); @@ -2085,7 +2092,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode ModelVolume *new_volume = model_object.add_volume(std::move(mesh), type); // First (any) GLVolume of the selected instance. They all share the same instance matrix. - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* v = selection.get_first_volume(); // Transform the new modifier to be aligned with the print bed. const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box(); new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); @@ -2810,7 +2817,7 @@ void ObjectList::merge(bool to_multipart_object) } } -void ObjectList::merge_volumes() +/*void ObjectList::merge_volumes() { std::vector obj_idxs, vol_idxs; get_selection_indexes(obj_idxs, vol_idxs); @@ -2838,11 +2845,11 @@ void ObjectList::merge_volumes() else { for (int vol_idx : vol_idxs) selection.add_volume(last_obj_idx, vol_idx, 0, false); - }*/ + }#1# #else wxGetApp().plater()->merge(obj_idxs[0], vol_idxs); #endif -} +}*/ void ObjectList::layers_editing() { @@ -3029,6 +3036,93 @@ bool ObjectList::can_split_instances() return selection.is_multiple_full_instance() || selection.is_single_full_instance(); } +bool ObjectList::has_selected_cut_object() const +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.IsEmpty()) + return false; + + for (wxDataViewItem item : sels) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + // ys_FIXME: The obj_idx= 0 && obj_idx < int(m_objects->size()) && object(obj_idx)->is_cut()) + return true; + } + + return false; +} + +void ObjectList::invalidate_cut_info_for_selection() +{ + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + invalidate_cut_info_for_object(size_t(obj_idx)); + } +} + +void ObjectList::invalidate_cut_info_for_object(int obj_idx) +{ + ModelObject* init_obj = object(obj_idx); + if (!init_obj->is_cut()) + return; + + take_snapshot(_u8L("Invalidate cut info")); + + const CutObjectBase cut_id = init_obj->cut_id; + // invalidate cut for related objects (which have the same cut_id) + for (size_t idx = 0; idx < m_objects->size(); idx++) + if (ModelObject* obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) { + obj->invalidate_cut(); + update_info_items(idx); + add_volumes_to_object_in_list(idx); + } + + update_lock_icons_for_model(); +} + +void ObjectList::delete_all_connectors_for_selection() +{ + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + delete_all_connectors_for_object(size_t(obj_idx)); + } +} + +void ObjectList::delete_all_connectors_for_object(int obj_idx) +{ + ModelObject* init_obj = object(obj_idx); + if (!init_obj->is_cut()) + return; + + take_snapshot(_u8L("Delete all connectors")); + + const CutObjectBase cut_id = init_obj->cut_id; + // Delete all connectors for related objects (which have the same cut_id) + Model& model = wxGetApp().plater()->model(); + for (int idx = int(m_objects->size())-1; idx >= 0; idx--) + if (ModelObject* obj = object(idx); obj->cut_id.is_equal(cut_id)) { + obj->delete_connectors(); + + if (obj->volumes.empty() || !obj->has_solid_mesh()) { + model.delete_object(idx); + m_objects_model->Delete(m_objects_model->GetItemById(idx)); + continue; + } + + update_info_items(idx); + add_volumes_to_object_in_list(idx); + changed_object(int(idx)); + } + + update_lock_icons_for_model(); +} + bool ObjectList::can_merge_to_multipart_object() const { if (has_selected_cut_object()) @@ -3043,10 +3137,9 @@ bool ObjectList::can_merge_to_multipart_object() const return false; // should be selected just objects - for (wxDataViewItem item : sels) { + for (wxDataViewItem item : sels) if (!(m_objects_model->GetItemType(item) & (itObject | itInstance))) return false; - } return true; } @@ -3071,97 +3164,6 @@ bool ObjectList::can_mesh_boolean() const return (*m_objects)[obj_idx]->volumes.size() > 1 || ((*m_objects)[obj_idx]->volumes.size() == 1 && (*m_objects)[obj_idx]->volumes[0]->is_splittable()); } -bool ObjectList::has_selected_cut_object() const -{ - wxDataViewItemArray sels; - GetSelections(sels); - if (sels.IsEmpty()) - return false; - - for (wxDataViewItem item : sels) { - const int obj_idx = m_objects_model->GetObjectIdByItem(item); - if (obj_idx >= 0 && object(obj_idx)->is_cut()) - return true; - } - - return false; -} - -void ObjectList::invalidate_cut_info_for_selection() -{ - const wxDataViewItem item = GetSelection(); - if (item) { - const int obj_idx = m_objects_model->GetObjectIdByItem(item); - if (obj_idx >= 0) - invalidate_cut_info_for_object(size_t(obj_idx)); - } -} - -void ObjectList::invalidate_cut_info_for_object(int obj_idx) -{ - ModelObject *init_obj = object(obj_idx); - if (!init_obj->is_cut()) return; - - take_snapshot("Invalidate cut info"); - - const CutObjectBase cut_id = init_obj->cut_id; - // invalidate cut for related objects (which have the same cut_id) - for (size_t idx = 0; idx < m_objects->size(); idx++) - if (ModelObject *obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) { - obj->invalidate_cut(); - update_info_items(idx); - add_volumes_to_object_in_list(idx); - } - - update_lock_icons_for_model(); -} - -void ObjectList::delete_all_connectors_for_selection() -{ - const wxDataViewItem item = GetSelection(); - if (item) { - const int obj_idx = m_objects_model->GetObjectIdByItem(item); - if (obj_idx >= 0) - delete_all_connectors_for_object(size_t(obj_idx)); - } -} - -void ObjectList::delete_all_connectors_for_object(int obj_idx) -{ - ModelObject *init_obj = object(obj_idx); - if (!init_obj->is_cut()) - return; - - take_snapshot("Delete all connectors"); - - auto has_solid_mesh = [](ModelObject* obj) { - for (const ModelVolume *volume : obj->volumes) - if (volume->is_model_part()) return true; - return false; - }; - - const CutObjectBase cut_id = init_obj->cut_id; - // Delete all connectors for related objects (which have the same cut_id) - Model &model = wxGetApp().plater()->model(); - for (int idx = int(m_objects->size()) - 1; idx >= 0; idx--) - if (ModelObject *obj = object(idx); obj->cut_id.is_equal(cut_id)) { - obj->delete_connectors(); - - if (obj->volumes.empty() || !has_solid_mesh(obj)) { - model.delete_object(idx); - m_objects_model->Delete(m_objects_model->GetItemById(idx)); - continue; - } - - update_info_items(idx); - add_volumes_to_object_in_list(idx); - changed_object(int(idx)); - } - - update_lock_icons_for_model(); -} - - // NO_PARAMETERS function call means that changed object index will be determine from Selection() void ObjectList::changed_object(const int obj_idx/* = -1*/) const { @@ -4349,7 +4351,7 @@ void ObjectList::update_selections() sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); } else if (selection.is_single_volume() || selection.is_any_modifier()) { - const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin()); + const auto gl_vol = selection.get_first_volume(); if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx()) return; } @@ -4425,8 +4427,7 @@ void ObjectList::update_selections() { if (m_selection_mode & smSettings) { - const auto idx = *selection.get_volume_idxs().begin(); - const auto gl_vol = selection.get_volume(idx); + const auto gl_vol = selection.get_first_volume(); if (gl_vol->volume_idx() >= 0) { // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids // are not associated with ModelVolumes, but they are temporarily generated by the backend diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 125c748037..8bd1e8fbdf 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -205,7 +205,7 @@ private: public: ObjectList(wxWindow* parent); - ~ObjectList(); + ~ObjectList() override; void set_min_height(); void update_min_height(); @@ -297,7 +297,7 @@ public: void del_info_item(const int obj_idx, InfoItemType type); void split(); void merge(bool to_multipart_object); - void merge_volumes(); // BBS: merge parts to single part + // void merge_volumes(); // BBS: merge parts to single part void layers_editing(); void boolean(); // BBS: Boolean Operation of parts diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index e808749e5a..35320a9c4d 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -2813,13 +2813,13 @@ int ObjectTablePanel::init_filaments_and_colors() } unsigned int i = 0; - unsigned char rgb[3]; + ColorRGB rgb; while (i < m_filaments_count) { const std::string& txt_color = global_config->opt_string("filament_colour", i); if (i < color_count) { - if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) + if (decode_color(txt_color, rgb)) { - m_filaments_colors[i] = wxColour(rgb[0], rgb[1], rgb[2]); + m_filaments_colors[i] = wxColour(rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar()); } else { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index fc10e765e3..3c76e7d9d8 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -2,7 +2,6 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Layer.hpp" #include "IMSlider.hpp" -#include "IMSlider_Utils.hpp" #include "GUI_Preview.hpp" #include "GUI_App.hpp" #include "GUI.hpp" diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index bf2c4277b8..6fbec69ba7 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GUI_Utils_hpp_ #define slic3r_GUI_Utils_hpp_ @@ -23,6 +27,7 @@ #include "Event.hpp" #include "../libslic3r/libslic3r_version.h" #include "../libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" class wxCheckBox; @@ -39,28 +44,10 @@ inline int hex_to_int(const char c) return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } -static std::array decode_color_to_float_array(const std::string color) +static ColorRGBA decode_color_to_float_array(const std::string color) { - // set alpha to 1.0f by default - std::array ret = {0, 0, 0, 1.0f}; - const char * c = color.data() + 1; - if (color.size() == 7 && color.front() == '#') { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_to_int(*c++); - int digit2 = hex_to_int(*c++); - if (digit1 == -1 || digit2 == -1) break; - ret[j] = float(digit1 * 16 + digit2) / 255.0f; - } - } - else if (color.size() == 9 && color.front() == '#') { - for (size_t j = 0; j < 4; ++j) { - int digit1 = hex_to_int(*c++); - int digit2 = hex_to_int(*c++); - if (digit1 == -1 || digit2 == -1) break; - - ret[j] = float(digit1 * 16 + digit2) / 255.0f; - } - } + ColorRGBA ret = ColorRGBA::BLACK(); + decode_color(color, ret); return ret; } @@ -470,14 +457,6 @@ public: std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics); -inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - class TaskTimer { std::chrono::milliseconds start_timer; @@ -488,6 +467,16 @@ public: ~TaskTimer(); }; +class KeyAutoRepeatFilter +{ + size_t m_count{ 0 }; + +public: + void increase_count() { ++m_count; } + void reset_count() { m_count = 0; } + bool is_first() const { return m_count == 0; } +}; + /* Image Generator */ #define _3MF_COVER_SIZE wxSize(240, 240) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index ba97805013..be0c2e1518 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -17,6 +17,8 @@ #include +#include "slic3r/GUI/CameraUtils.hpp" + namespace Slic3r { namespace GUI { @@ -27,32 +29,12 @@ const int c_connectors_group_id = 4; const float UndefFloat = -999.f; // connector colors - -using ColorRGBA = std::array; - -static const ColorRGBA BLACK() { return {0.0f, 0.0f, 0.0f, 1.0f}; } -static const ColorRGBA BLUE() { return {0.0f, 0.0f, 1.0f, 1.0f}; } -static const ColorRGBA BLUEISH() { return {0.5f, 0.5f, 1.0f, 1.0f}; } -static const ColorRGBA CYAN() { return {0.0f, 1.0f, 1.0f, 1.0f}; } -static const ColorRGBA DARK_GRAY() { return {0.25f, 0.25f, 0.25f, 1.0f}; } -static const ColorRGBA DARK_YELLOW() { return {0.5f, 0.5f, 0.0f, 1.0f}; } -static const ColorRGBA GRAY() { return {0.5f, 0.5f, 0.5f, 1.0f}; } -static const ColorRGBA GREEN() { return {0.0f, 1.0f, 0.0f, 1.0f}; } -static const ColorRGBA GREENISH() { return {0.5f, 1.0f, 0.5f, 1.0f}; } -static const ColorRGBA LIGHT_GRAY() { return {0.75f, 0.75f, 0.75f, 1.0f}; } -static const ColorRGBA MAGENTA() { return {1.0f, 0.0f, 1.0f, 1.0f}; } -static const ColorRGBA ORANGE() { return {0.923f, 0.504f, 0.264f, 1.0f}; } -static const ColorRGBA RED() { return {1.0f, 0.0f, 0.0f, 1.0f}; } -static const ColorRGBA REDISH() { return {1.0f, 0.5f, 0.5f, 1.0f}; } -static const ColorRGBA YELLOW() { return {1.0f, 1.0f, 0.0f, 1.0f}; } -static const ColorRGBA WHITE() { return {1.0f, 1.0f, 1.0f, 1.0f}; } - -static const ColorRGBA PLAG_COLOR = YELLOW(); -static const ColorRGBA DOWEL_COLOR = DARK_YELLOW(); -static const ColorRGBA HOVERED_PLAG_COLOR = CYAN(); +static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW(); +static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW(); +static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN(); static const ColorRGBA HOVERED_DOWEL_COLOR = {0.0f, 0.5f, 0.5f, 1.0f}; -static const ColorRGBA SELECTED_PLAG_COLOR = GRAY(); -static const ColorRGBA SELECTED_DOWEL_COLOR = GRAY(); // DARK_GRAY(); +static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY(); +static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::GRAY(); // DARK_GRAY(); static const ColorRGBA CONNECTOR_DEF_COLOR = {1.0f, 1.0f, 1.0f, 0.5f}; static const ColorRGBA CONNECTOR_ERR_COLOR = {1.0f, 0.3f, 0.3f, 0.5f}; static const ColorRGBA HOVERED_ERR_COLOR = {1.0f, 0.3f, 0.3f, 1.0f}; @@ -103,8 +85,8 @@ static void rotate_z_3d(std::array& verts, float radian_angle) const double GLGizmoAdvancedCut::Offset = 10.0; const double GLGizmoAdvancedCut::Margin = 20.0; -const std::array GLGizmoAdvancedCut::GrabberColor = { 1.0, 1.0, 0.0, 1.0 }; -const std::array GLGizmoAdvancedCut::GrabberHoverColor = { 0.7, 0.7, 0.0, 1.0}; +const ColorRGBA GLGizmoAdvancedCut::GrabberColor = { 1.0f, 1.0f, 0.0f, 1.0f }; +const ColorRGBA GLGizmoAdvancedCut::GrabberHoverColor = { 0.7f, 0.7f, 0.0f, 1.0f }; GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoRotate3D(parent, icon_filename, sprite_id, nullptr) @@ -124,13 +106,19 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic for (int i = 0; i < 4; i++) m_cut_plane_points[i] = { 0., 0., 0. }; - set_group_id(m_gizmos.size()); + m_group_id = (m_gizmos.size()); m_rotation.setZero(); //m_current_base_rotation.setZero(); m_rotate_cmds.clear(); m_buffered_rotation.setZero(); } +void GLGizmoAdvancedCut::data_changed(bool is_serializing) +{ + GLGizmoRotate3D::data_changed(is_serializing); + finish_rotation(); +} + bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) { CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; @@ -146,7 +134,7 @@ bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mous return false; if (m_hover_id != -1) { - start_dragging(); + //start_dragging(); return true; } @@ -241,7 +229,7 @@ bool GLGizmoAdvancedCut::unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &p Vec3d point; Vec3d direction; Vec3d hit; - MeshRaycaster::line_from_mouse_pos_static(mouse_pos, Transform3d::Identity(), camera, point, direction); + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Vec3d normal = -cp->get_normal().cast(); double den = normal.dot(direction); if (den != 0.) { @@ -431,38 +419,32 @@ CommonGizmosDataID GLGizmoAdvancedCut::on_get_requirements() const void GLGizmoAdvancedCut::on_start_dragging() { - for (auto gizmo : m_gizmos) { - if (m_hover_id == gizmo.get_group_id()) { - gizmo.start_dragging(); - return; - } + if (m_hover_id == X || m_hover_id == Y || m_hover_id == Z) { + m_gizmos[m_hover_id].start_dragging(); + } else if (m_hover_id == c_connectors_group_id - 1) { + const Selection& selection = m_parent.get_selection(); + const BoundingBoxf3& box = selection.get_bounding_box(); + m_start_movement = m_movement; + m_start_height = m_height; + m_drag_pos = m_move_grabber.center; } - - if (m_hover_id != get_group_id()) - return; - - const Selection& selection = m_parent.get_selection(); - const BoundingBoxf3& box = selection.get_bounding_box(); - m_start_movement = m_movement; - m_start_height = m_height; - m_drag_pos = m_move_grabber.center; - - if (m_hover_id >= c_connectors_group_id) - Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move connector"); } void GLGizmoAdvancedCut::on_stop_dragging() { if (m_hover_id == X || m_hover_id == Y || m_hover_id == Z) { + m_gizmos[m_hover_id].stop_dragging(); Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Rotate cut plane"); } else if (m_hover_id == c_connectors_group_id - 1) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move cut plane"); + } else if (m_hover_id >= c_connectors_group_id) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move connector"); } } -void GLGizmoAdvancedCut::on_update(const UpdateData& data) +void GLGizmoAdvancedCut::on_dragging(const UpdateData &data) { - GLGizmoRotate3D::on_update(data); + GLGizmoRotate3D::on_dragging(data); Vec3d rotation; for (int i = 0; i < 3; i++) @@ -475,7 +457,7 @@ void GLGizmoAdvancedCut::on_update(const UpdateData& data) m_rotation = rotation; //m_move_grabber.angles = m_current_base_rotation + m_rotation; - if (m_hover_id == get_group_id()) { + if (m_hover_id == m_group_id) { double move = calc_projection(data.mouse_ray); set_movement(m_start_movement + move); Vec3d plane_normal = get_plane_normal(); @@ -511,7 +493,7 @@ void GLGizmoAdvancedCut::on_render() render_cut_line(); } - +/* void GLGizmoAdvancedCut::on_render_for_picking() { GLGizmoRotate3D::on_render_for_picking(); @@ -525,12 +507,17 @@ void GLGizmoAdvancedCut::on_render_for_picking() float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); #endif - std::array color = picking_color_component(0); - m_move_grabber.color[0] = color[0]; - m_move_grabber.color[1] = color[1]; - m_move_grabber.color[2] = color[2]; - m_move_grabber.color[3] = color[3]; - m_move_grabber.render_for_picking(mean_size); + m_move_grabber.color = picking_color_component(0); + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + m_move_grabber.render_for_picking(mean_size); + + shader->stop_using(); + } glsafe(::glEnable(GL_DEPTH_TEST)); auto inst_id = m_c->selection_info()->get_active_instance(); @@ -548,7 +535,6 @@ void GLGizmoAdvancedCut::on_render_for_picking() Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); float height = connector.height; - const Camera &camera = wxGetApp().plater()->get_camera(); if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prizm) { pos -= height * m_cut_plane_normal; height *= 2; @@ -561,14 +547,13 @@ void GLGizmoAdvancedCut::on_render_for_picking() Transform3d scale_tf = Transform3d::Identity(); scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast()); - const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf; + const Transform3d model_matrix = translate_tf * m_rotate_matrix * scale_tf; - - std::array color = picking_color_component(i+1); - render_connector_model(m_shapes[connectors[i].attribs], color, view_model_matrix, true); + ColorRGBA color = picking_color_component(i+1); + render_connector_model(m_shapes[connectors[i].attribs], color, model_matrix, true); } } - +*/ void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_limit) { GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); @@ -638,7 +623,7 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection) wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoAdvancedCut: Invalid object selection"); // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* first_glvolume = selection.get_first_volume(); // perform cut { @@ -882,67 +867,117 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers() point += object_offset; } - // draw plane glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - for (const Vec3d& point : plane_points_rot) { - ::glVertex3f(point(0), point(1), point(2)); - } - glsafe(::glEnd()); + // draw plane + { + m_plane.reset(); - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; + init_data.reserve_vertices(4); + init_data.reserve_vertices(6); - // Draw the grabber and the connecting line - Vec3d plane_center_rot = calc_plane_center(plane_points_rot); - m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset; - // m_move_grabber.angles = m_current_base_rotation + m_rotation; + // vertices + for (const Vec3d &point : plane_points_rot) { + init_data.add_vertex((Vec3f)point.cast()); + } - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); - glsafe(::glColor3f(1.0, 1.0, 0.0)); - glLineStipple(1, 0x0FFF); - glEnable(GL_LINE_STIPPLE); - ::glBegin(GL_LINES); - ::glVertex3dv(plane_center_rot.data()); - ::glVertex3dv(m_move_grabber.center.data()); - glsafe(::glEnd()); - glDisable(GL_LINE_STIPPLE); + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); - // std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color); - // m_move_grabber.color = GrabberColor; - // m_move_grabber.hover_color = GrabberHoverColor; - // m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); - bool hover = (m_hover_id == get_group_id()); - std::array render_color; - if (hover) { - render_color = GrabberHoverColor; - } - else - render_color = GrabberColor; + m_plane.init_from(std::move(init_data)); + } + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + m_plane.render(); - const GLModel &cube = m_move_grabber.get_cube(); - // BBS set to fixed size grabber - // float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); - float fullsize = 8.0f; - if (GLGizmoBase::INV_ZOOM > 0) { - fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM; + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + + // Draw the grabber and the connecting line + Vec3d plane_center_rot = calc_plane_center(plane_points_rot); + m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset; + // m_move_grabber.angles = m_current_base_rotation + m_rotation; + + { + m_grabber_connection.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::YELLOW(); + init_data.reserve_vertices(2); + init_data.reserve_vertices(2); + + // vertices + init_data.add_vertex((Vec3f)plane_center_rot.cast()); + init_data.add_vertex((Vec3f)m_move_grabber.center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connection.init_from(std::move(init_data)); + } + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); + glLineStipple(1, 0x0FFF); + glEnable(GL_LINE_STIPPLE); + m_grabber_connection.render(); + glDisable(GL_LINE_STIPPLE); + + shader->stop_using(); } - const_cast(&cube)->set_color(-1, render_color); + { + GLShaderProgram *shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + // std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color); + // m_move_grabber.color = GrabberColor; + // m_move_grabber.hover_color = GrabberHoverColor; + // m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); + bool hover = (m_hover_id == m_group_id); + ColorRGBA render_color; + if (hover) { + render_color = GrabberHoverColor; + } + else + render_color = GrabberColor; - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_move_grabber.center.x(), m_move_grabber.center.y(), m_move_grabber.center.z())); - glsafe(::glMultMatrixd(m_rotate_matrix.data())); + PickingModel &cube = m_move_grabber.get_cube(); + // BBS set to fixed size grabber + // float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); + float fullsize = 8.0f; + if (GLGizmoBase::INV_ZOOM > 0) { + fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM; + } - glsafe(::glScaled(fullsize, fullsize, fullsize)); - cube.render(); - glsafe(::glPopMatrix()); + cube.model.set_color(render_color); + + const Transform3d trafo_matrix = Geometry::assemble_transform(m_move_grabber.center) * m_rotate_matrix * + Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), fullsize * Vec3d::Ones()); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + cube.model.render(); + shader->stop_using(); + } // Should be placed at last, because GLGizmoRotate3D clears depth buffer set_center(m_cut_plane_center); @@ -1007,9 +1042,9 @@ void GLGizmoAdvancedCut::render_connectors() Transform3d scale_tf = Transform3d::Identity(); scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast()); - const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf; + const Transform3d model_matrix = translate_tf * m_rotate_matrix * scale_tf; - render_connector_model(m_shapes[connector.attribs], render_color, view_model_matrix); + render_connector_model(m_shapes[connector.attribs], render_color, model_matrix); } } @@ -1025,36 +1060,65 @@ void GLGizmoAdvancedCut::render_cut_line() if (!cut_line_processing() || m_cut_line_end == Vec3d::Zero()) return; - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glColor3f(0.0, 1.0, 0.0)); - glEnable(GL_LINE_STIPPLE); - ::glBegin(GL_LINES); - ::glVertex3dv(m_cut_line_begin.data()); - ::glVertex3dv(m_cut_line_end.data()); - glsafe(::glEnd()); - glDisable(GL_LINE_STIPPLE); + glsafe(::glDisable(GL_DEPTH_TEST)); + + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + { + m_cut_line.reset(); + + GLModel::Geometry init_data; + init_data.format = {GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3}; + init_data.color = ColorRGBA::GREEN(); + init_data.reserve_vertices(2); + init_data.reserve_vertices(2); + + // vertices + init_data.add_vertex((Vec3f) m_cut_line_begin.cast()); + init_data.add_vertex((Vec3f) m_cut_line_end.cast()); + + // indices + init_data.add_line(0, 1); + + m_cut_line.init_from(std::move(init_data)); + } + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, 0x0FFF); + m_cut_line.render(); + glDisable(GL_LINE_STIPPLE); + + shader->stop_using(); + } } -void GLGizmoAdvancedCut::render_connector_model(GLModel &model, const std::array &color, Transform3d view_model_matrix, bool for_picking) +void GLGizmoAdvancedCut::render_connector_model(GLModel &model, const ColorRGBA &color, Transform3d model_matrix, bool for_picking) { - glPushMatrix(); GLShaderProgram *shader = nullptr; if (for_picking) - shader = wxGetApp().get_shader("cali"); + shader = wxGetApp().get_shader("flat"); else shader = wxGetApp().get_shader("gouraud_light"); if (shader) { shader->start_using(); - glsafe(::glMultMatrixd(view_model_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); - model.set_color(-1, color); + model.set_color(color); model.render(); shader->stop_using(); } - glPopMatrix(); } void GLGizmoAdvancedCut::clear_selection() @@ -1814,7 +1878,7 @@ bool GLGizmoAdvancedCut::process_cut_line(SLAGizmoEventType action, const Vec2d Vec3d pt; Vec3d dir; - MeshRaycaster::line_from_mouse_pos_static(mouse_position, Transform3d::Identity(), camera, pt, dir); + CameraUtils::ray_from_screen_pos(camera, mouse_position, pt, dir); dir.normalize(); pt += dir; // Move the pt along dir so it is not clipped. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp index 5cb9e765cc..d791663ea9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp @@ -27,8 +27,8 @@ struct Rotate_data { private: static const double Offset; static const double Margin; - static const std::array GrabberColor; - static const std::array GrabberHoverColor; + static const ColorRGBA GrabberColor; + static const ColorRGBA GrabberHoverColor; mutable double m_movement; mutable double m_height; // height of cut plane to heatbed @@ -53,6 +53,9 @@ private: bool m_place_on_cut_lower{false}; bool m_rotate_upper{false}; bool m_rotate_lower{false}; + GLModel m_plane; + GLModel m_grabber_connection; + GLModel m_cut_line; bool m_do_segment; double m_segment_smoothing_alpha; @@ -135,45 +138,24 @@ public: virtual bool apply_clipping_plane() { return m_connectors_editing; } + void data_changed(bool is_serializing) override; + protected: - virtual bool on_init(); - virtual void on_load(cereal::BinaryInputArchive &ar) override; - virtual void on_save(cereal::BinaryOutputArchive &ar) const override; - virtual std::string on_get_name() const; - virtual void on_set_state(); - virtual bool on_is_activable() const; - virtual CommonGizmosDataID on_get_requirements() const override; - virtual void on_start_dragging() override; - virtual void on_stop_dragging() override; - virtual void on_update(const UpdateData& data); - virtual void on_render(); - virtual void on_render_for_picking(); + bool on_init() override; + void on_load(cereal::BinaryInputArchive &ar) override; + void on_save(cereal::BinaryOutputArchive &ar) const override; + std::string on_get_name() const override; + void on_set_state() override; + bool on_is_activable() const override; + CommonGizmosDataID on_get_requirements() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_dragging(const UpdateData& data) override; + void on_render() override; virtual void on_render_input_window(float x, float y, float bottom_limit); void show_tooltip_information(float x, float y); - virtual void on_enable_grabber(unsigned int id) - { - if (id < 3) - m_gizmos[id].enable_grabber(0); - else if (id == 3) - this->enable_grabber(0); - } - - virtual void on_disable_grabber(unsigned int id) - { - if (id < 3) - m_gizmos[id].disable_grabber(0); - else if (id == 3) - this->disable_grabber(0); - } - - virtual void on_set_hover_id() - { - for (int i = 0; i < 3; ++i) - m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); - } - private: void perform_cut(const Selection& selection); bool can_perform_cut() const; @@ -201,7 +183,7 @@ private: void render_connectors(); void render_clipper_cut(); void render_cut_line(); - void render_connector_model(GLModel &model, const std::array& color, Transform3d view_model_matrix, bool for_picking = false); + void render_connector_model(GLModel &model, const ColorRGBA& color, Transform3d model_matrix, bool for_picking = false); void clear_selection(); void init_connector_shapes(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index b1d98d3dc2..ed98b81931 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -1,9 +1,14 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_Colors.hpp" // TODO: Display tooltips quicker on Linux @@ -21,73 +26,63 @@ const float GLGizmoBase::Grabber::FixedGrabberSize = 16.0f; const float GLGizmoBase::Grabber::FixedRadiusSize = 80.0f; -std::array GLGizmoBase::DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }; -std::array GLGizmoBase::DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; -std::array GLGizmoBase::DEFAULT_HIGHLIGHT_COLOR = { 1.0f, 0.38f, 0.0f, 1.0f }; -std::array, 3> GLGizmoBase::AXES_HOVER_COLOR = {{ +ColorRGBA GLGizmoBase::DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }; +ColorRGBA GLGizmoBase::DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; +ColorRGBA GLGizmoBase::DEFAULT_HIGHLIGHT_COLOR = {1.0f, 0.38f, 0.0f, 1.0f}; +std::array GLGizmoBase::AXES_HOVER_COLOR = {{ { 0.7f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.7f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.7f, 1.0f } }}; -std::array, 3> GLGizmoBase::AXES_COLOR = { { +std::array GLGizmoBase::AXES_COLOR = {{ { 1.0, 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }}; -std::array GLGizmoBase::CONSTRAINED_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; -std::array GLGizmoBase::FLATTEN_COLOR = { 0.96f, 0.93f, 0.93f, 0.5f }; -std::array GLGizmoBase::FLATTEN_HOVER_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; +ColorRGBA GLGizmoBase::CONSTRAINED_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}; +ColorRGBA GLGizmoBase::FLATTEN_COLOR = {0.96f, 0.93f, 0.93f, 0.5f}; +ColorRGBA GLGizmoBase::FLATTEN_HOVER_COLOR = {1.0f, 1.0f, 1.0f, 0.75f}; // new style color -std::array GLGizmoBase::GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f}; -std::array GLGizmoBase::GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f}; -std::array GLGizmoBase::GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f}; -std::array GLGizmoBase::GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f}; +ColorRGBA GLGizmoBase::GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f}; +ColorRGBA GLGizmoBase::GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f}; +ColorRGBA GLGizmoBase::GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f}; +ColorRGBA GLGizmoBase::GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f}; void GLGizmoBase::update_render_colors() { GLGizmoBase::AXES_COLOR = { { - GLColor(RenderColor::colors[RenderCol_Grabber_X]), - GLColor(RenderColor::colors[RenderCol_Grabber_Y]), - GLColor(RenderColor::colors[RenderCol_Grabber_Z]) + ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_X]), + ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_Y]), + ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_Z]) } }; - GLGizmoBase::FLATTEN_COLOR = GLColor(RenderColor::colors[RenderCol_Flatten_Plane]); - GLGizmoBase::FLATTEN_HOVER_COLOR = GLColor(RenderColor::colors[RenderCol_Flatten_Plane_Hover]); + GLGizmoBase::FLATTEN_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Flatten_Plane]); + GLGizmoBase::FLATTEN_HOVER_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Flatten_Plane_Hover]); } void GLGizmoBase::load_render_colors() { - RenderColor::colors[RenderCol_Grabber_X] = IMColor(GLGizmoBase::AXES_COLOR[0]); - RenderColor::colors[RenderCol_Grabber_Y] = IMColor(GLGizmoBase::AXES_COLOR[1]); - RenderColor::colors[RenderCol_Grabber_Z] = IMColor(GLGizmoBase::AXES_COLOR[2]); - RenderColor::colors[RenderCol_Flatten_Plane] = IMColor(GLGizmoBase::FLATTEN_COLOR); - RenderColor::colors[RenderCol_Flatten_Plane_Hover] = IMColor(GLGizmoBase::FLATTEN_HOVER_COLOR); + RenderColor::colors[RenderCol_Grabber_X] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[0]); + RenderColor::colors[RenderCol_Grabber_Y] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[1]); + RenderColor::colors[RenderCol_Grabber_Z] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[2]); + RenderColor::colors[RenderCol_Flatten_Plane] = ImGuiWrapper::to_ImVec4(GLGizmoBase::FLATTEN_COLOR); + RenderColor::colors[RenderCol_Flatten_Plane_Hover] = ImGuiWrapper::to_ImVec4(GLGizmoBase::FLATTEN_HOVER_COLOR); } -GLGizmoBase::Grabber::Grabber() - : center(Vec3d::Zero()) - , angles(Vec3d::Zero()) - , dragging(false) - , enabled(true) -{ - color = GRABBER_NORMAL_COL; - hover_color = GRABBER_HOVER_COL; -} +PickingModel GLGizmoBase::Grabber::s_cube; +PickingModel GLGizmoBase::Grabber::s_cone; -void GLGizmoBase::Grabber::render(bool hover, float size) const +GLGizmoBase::Grabber::~Grabber() { - std::array render_color; - if (hover) { - render_color = hover_color; - } - else - render_color = color; + if (s_cube.model.is_initialized()) + s_cube.model.reset(); - render(size, render_color, false); + if (s_cone.model.is_initialized()) + s_cone.model.reset(); } float GLGizmoBase::Grabber::get_half_size(float size) const @@ -100,70 +95,124 @@ float GLGizmoBase::Grabber::get_dragging_half_size(float size) const return get_half_size(size) * DraggingScaleFactor; } -const GLModel& GLGizmoBase::Grabber::get_cube() const +PickingModel &GLGizmoBase::Grabber::get_cube() { - if (! cube_initialized) { + if (!s_cube.model.is_initialized()) { // This cannot be done in constructor, OpenGL is not yet // initialized at that point (on Linux at least). - indexed_triangle_set mesh = its_make_cube(1., 1., 1.); - its_translate(mesh, Vec3f(-0.5, -0.5, -0.5)); - const_cast(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); - const_cast(cube_initialized) = true; + indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); + its_translate(its, -0.5f * Vec3f::Ones()); + s_cube.model.init_from(its); + s_cube.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } - return cube; + return s_cube; } -void GLGizmoBase::Grabber::render(float size, const std::array& render_color, bool picking) const +void GLGizmoBase::Grabber::register_raycasters_for_picking(int id) { - if (! cube_initialized) { + picking_id = id; + // registration will happen on next call to render() +} + +void GLGizmoBase::Grabber::unregister_raycasters_for_picking() +{ + wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, picking_id); + picking_id = -1; + raycasters = { nullptr }; +} + +void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color) +{ + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr) + return; + + if (!s_cube.model.is_initialized()) { // This cannot be done in constructor, OpenGL is not yet // initialized at that point (on Linux at least). - indexed_triangle_set mesh = its_make_cube(1., 1., 1.); - its_translate(mesh, Vec3f(-0.5, -0.5, -0.5)); - const_cast(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); - const_cast(cube_initialized) = true; + indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); + its_translate(its, -0.5f * Vec3f::Ones()); + s_cube.model.init_from(its); + s_cube.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + + if (!s_cone.model.is_initialized()) { + indexed_triangle_set its = its_make_cone(1.0, 1.0, double(PI) / 18.0); + s_cone.model.init_from(its); + s_cone.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } //BBS set to fixed size grabber - //float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); - float fullsize = 8.0f; - if (GLGizmoBase::INV_ZOOM > 0) { - fullsize = FixedGrabberSize * GLGizmoBase::INV_ZOOM; + const float grabber_size = FixedGrabberSize * INV_ZOOM; + const double extension_size = 0.75 * FixedGrabberSize * INV_ZOOM; + + s_cube.model.set_color(render_color); + s_cone.model.set_color(render_color); + + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Matrix3d view_matrix_no_offset = view_matrix.matrix().block(0, 0, 3, 3); + + auto render_extension = [&view_matrix, &view_matrix_no_offset, shader, this](int idx, PickingModel &model, const Transform3d &model_matrix) { + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix_no_offset * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + model.model.render(); + + if (raycasters[idx] == nullptr) { + GLCanvas3D &canvas = *wxGetApp().plater()->canvas3D(); + raycasters[idx] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *model.mesh_raycaster, model_matrix); + } else { + raycasters[idx]->set_transform(model_matrix); + } + }; + + if (extensions == EGrabberExtension::PosZ) { + const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, Vec3d(0.75 * extension_size, 0.75 * extension_size, 2.0 * extension_size)); + render_extension(0, s_cone, model_matrix); + } else { + const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, grabber_size * Vec3d::Ones()); + render_extension(0, s_cube, model_matrix); + + const Transform3d extension_model_matrix_base = matrix * Geometry::assemble_transform(center, angles); + const Vec3d extension_scale(0.75 * extension_size, 0.75 * extension_size, 3.0 * extension_size); + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) { + render_extension(1, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitX(), Vec3d(0.0, 0.5 * double(PI), 0.0), extension_scale)); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) { + render_extension(2, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitX(), Vec3d(0.0, -0.5 * double(PI), 0.0), extension_scale)); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) { + render_extension(3, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitY(), Vec3d(-0.5 * double(PI), 0.0, 0.0), extension_scale)); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) { + render_extension(4, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitY(), Vec3d(0.5 * double(PI), 0.0, 0.0), extension_scale)); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) { + render_extension(5, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitZ(), Vec3d::Zero(), extension_scale)); + } + if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) { + render_extension(6, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitZ(), Vec3d(double(PI), 0.0, 0.0), extension_scale)); + } } - - - const_cast(&cube)->set_color(-1, render_color); - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - glsafe(::glScaled(fullsize, fullsize, fullsize)); - cube.render(); - glsafe(::glPopMatrix()); } - GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : m_parent(parent) , m_group_id(-1) , m_state(Off) - , m_shortcut_key(0) + , m_shortcut_key(NO_SHORTCUT_KEY_VALUE) , m_icon_filename(icon_filename) , m_sprite_id(sprite_id) - , m_hover_id(-1) - , m_dragging(false) , m_imgui(wxGetApp().imgui()) - , m_first_input_window_render(true) - , m_dirty(false) { - m_base_color = DEFAULT_BASE_COLOR; - m_drag_color = DEFAULT_DRAG_COLOR; - m_highlight_color = DEFAULT_HIGHLIGHT_COLOR; - m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24)); - m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.)); - m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.)); +} + + +std::string GLGizmoBase::get_action_snapshot_name() const +{ + return "Gizmo action"; } void GLGizmoBase::set_icon_filename(const std::string &filename) { @@ -172,64 +221,15 @@ void GLGizmoBase::set_icon_filename(const std::string &filename) { void GLGizmoBase::set_hover_id(int id) { - if (m_grabbers.empty() || (id < (int)m_grabbers.size())) - { - m_hover_id = id; - on_set_hover_id(); - } -} + // do not change hover id during dragging + assert(!m_dragging); -void GLGizmoBase::set_highlight_color(const std::array& color) -{ - m_highlight_color = color; -} - -void GLGizmoBase::enable_grabber(unsigned int id) -{ - if (id < m_grabbers.size()) - m_grabbers[id].enabled = true; - - on_enable_grabber(id); -} - -void GLGizmoBase::disable_grabber(unsigned int id) -{ - if (id < m_grabbers.size()) - m_grabbers[id].enabled = false; - - on_disable_grabber(id); -} - -void GLGizmoBase::start_dragging() -{ - m_dragging = true; - - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - m_grabbers[i].dragging = (m_hover_id == i); - } - - on_start_dragging(); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("this %1%, m_hover_id=%2%\n")%this %m_hover_id; -} - -void GLGizmoBase::stop_dragging() -{ - m_dragging = false; - - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - m_grabbers[i].dragging = false; - } - - on_stop_dragging(); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("this %1%, m_hover_id=%2%\n")%this %m_hover_id; -} - -void GLGizmoBase::update(const UpdateData& data) -{ - if (m_hover_id != -1) - on_update(data); + // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate +// if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) +// return; + + m_hover_id = id; + on_set_hover_id(); } bool GLGizmoBase::update_items_state() @@ -237,7 +237,7 @@ bool GLGizmoBase::update_items_state() bool res = m_dirty; m_dirty = false; return res; -}; +} bool GLGizmoBase::GizmoImguiBegin(const std::string &name, int flags) { @@ -265,22 +265,18 @@ void GLGizmoBase::GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float m_imgui->set_next_window_pos(x, y, flag, pivot_x, pivot_y); } -std::array GLGizmoBase::picking_color_component(unsigned int id) const +void GLGizmoBase::register_grabbers_for_picking() { - static const float INV_255 = 1.0f / 255.0f; + for (size_t i = 0; i < m_grabbers.size(); ++i) { + m_grabbers[i].register_raycasters_for_picking((m_group_id >= 0) ? m_group_id : i); + } +} - id = BASE_ID - id; - - if (m_group_id > -1) - id -= m_group_id; - - // color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass() - return std::array { - float((id >> 0) & 0xff) * INV_255, // red - float((id >> 8) & 0xff) * INV_255, // green - float((id >> 16) & 0xff) * INV_255, // blue - float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 // checksum for validating against unwanted alpha blending and multi sampling - }; +void GLGizmoBase::unregister_grabbers_for_picking() +{ + for (size_t i = 0; i < m_grabbers.size(); ++i) { + m_grabbers[i].unregister_raycasters_for_picking(); + } } void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const @@ -293,34 +289,101 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const } void GLGizmoBase::render_grabbers(float size) const +{ + render_grabbers(0, m_grabbers.size() - 1, size, false); +} + +void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - for (int i = 0; i < (int)m_grabbers.size(); ++i) { + glsafe(::glDisable(GL_CULL_FACE)); + for (size_t i = first; i <= last; ++i) { if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i, size); + m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size); } + glsafe(::glEnable(GL_CULL_FACE)); shader->stop_using(); } -void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const -{ -#if ENABLE_FIXED_GRABBER - float mean_size = (float)(GLGizmoBase::Grabber::FixedGrabberSize); -#else - float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); -#endif +// help function to process grabbers +// call start_dragging, stop_dragging, on_dragging +bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { + bool is_dragging_finished = false; + if (mouse_event.Moving()) { + // it should not happen but for sure + assert(!m_dragging); + if (m_dragging) is_dragging_finished = true; + else return false; + } - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { - if (m_grabbers[i].enabled) { - std::array color = picking_color_component(i); - m_grabbers[i].color = color; - m_grabbers[i].render_for_picking(mean_size); + if (mouse_event.LeftDown()) { + Selection &selection = m_parent.get_selection(); + if (!selection.is_empty() && m_hover_id != -1 /* && + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))*/) { + selection.setup_cache(); + + m_dragging = true; + for (auto &grabber : m_grabbers) grabber.dragging = false; +// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) +// m_grabbers[m_hover_id].dragging = true; + + on_start_dragging(); + + // Let the plater know that the dragging started + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); + m_parent.set_as_dirty(); + return true; + } + } else if (m_dragging) { + // when mouse cursor leave window than finish actual dragging operation + bool is_leaving = mouse_event.Leaving(); + if (mouse_event.Dragging()) { + Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + auto ray = m_parent.mouse_ray(mouse_coord); + UpdateData data(ray, mouse_coord); + + on_dragging(data); + + wxGetApp().obj_manipul()->set_dirty(); + m_parent.set_as_dirty(); + return true; + } + else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) { + do_stop_dragging(is_leaving); + return true; } } + return false; +} + +void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup) +{ + for (auto& grabber : m_grabbers) grabber.dragging = false; + m_dragging = false; + + // NOTE: This should be part of GLCanvas3D + // Reset hover_id when leave window + if (perform_mouse_cleanup) m_parent.mouse_up_cleanup(); + + on_stop_dragging(); + + // There is prediction that after draggign, data are changed + // Data are updated twice also by canvas3D::reload_scene. + // Should be fixed. + m_parent.get_gizmos_manager().update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + + // Let the plater know that the dragging finished, so a delayed + // refresh of the scene with the background processing data should + // be performed. + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + m_parent.refresh_camera_scene_box(); } std::string GLGizmoBase::format(float value, unsigned int decimals) const @@ -336,9 +399,12 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) { on_render_input_window(x, y, bottom_limit); if (m_first_input_window_render) { - // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame - // so, we forces another frame rendering the first time the imgui window is shown + // imgui windows that don't have an initial size needs to be processed once to get one + // and are not rendered in the first frame + // so, we forces to render another frame the first time the imgui window is shown + // https://github.com/ocornut/imgui/issues/2949 m_parent.set_as_dirty(); + m_parent.request_extra_frame(); m_first_input_window_render = false; } } @@ -354,23 +420,5 @@ std::string GLGizmoBase::get_name(bool include_shortcut) const return out; } - - -// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components -// were not interpolated by alpha blending or multi sampling. -unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue) -{ - // 8 bit hash for the color - unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff; - // Increase enthropy by a bit reversal - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - // Flip every second bit to increase the enthropy even more. - b ^= 0x55; - return b; -} - - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index c725809bba..d60e504856 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -1,10 +1,17 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoBase_hpp_ #define slic3r_GLGizmoBase_hpp_ #include "libslic3r/Point.hpp" +#include "libslic3r/Color.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/SceneRaycaster.hpp" #include @@ -26,7 +33,6 @@ class ImGuiWrapper; class GLCanvas3D; enum class CommonGizmosDataID; class CommonGizmosDataPool; -class Selection; class GLGizmoBase { @@ -34,26 +40,41 @@ public: // Starting value for ids to avoid clashing with ids used by GLVolumes // (254 is choosen to leave some space for forward compatibility) static const unsigned int BASE_ID = 255 * 255 * 254; + static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7; static float INV_ZOOM; //BBS colors - static std::array DEFAULT_BASE_COLOR; - static std::array DEFAULT_DRAG_COLOR; - static std::array DEFAULT_HIGHLIGHT_COLOR; - static std::array, 3> AXES_COLOR; - static std::array, 3> AXES_HOVER_COLOR; - static std::array CONSTRAINED_COLOR; - static std::array FLATTEN_COLOR; - static std::array FLATTEN_HOVER_COLOR; - static std::array GRABBER_NORMAL_COL; - static std::array GRABBER_HOVER_COL; - static std::array GRABBER_UNIFORM_COL; - static std::array GRABBER_UNIFORM_HOVER_COL; + static ColorRGBA DEFAULT_BASE_COLOR; + static ColorRGBA DEFAULT_DRAG_COLOR; + static ColorRGBA DEFAULT_HIGHLIGHT_COLOR; + static std::array AXES_COLOR; + static std::array AXES_HOVER_COLOR; + static ColorRGBA CONSTRAINED_COLOR; + static ColorRGBA FLATTEN_COLOR; + static ColorRGBA FLATTEN_HOVER_COLOR; + static ColorRGBA GRABBER_NORMAL_COL; + static ColorRGBA GRABBER_HOVER_COL; + static ColorRGBA GRABBER_UNIFORM_COL; + static ColorRGBA GRABBER_UNIFORM_HOVER_COL; static void update_render_colors(); static void load_render_colors(); + enum class EGrabberExtension + { + None = 0, + PosX = 1 << 0, + NegX = 1 << 1, + PosY = 1 << 2, + NegY = 1 << 3, + PosZ = 1 << 4, + NegZ = 1 << 5, + }; + + // Represents NO key(button on keyboard) value + static const int NO_SHORTCUT_KEY_VALUE = 0; + protected: struct Grabber { @@ -63,27 +84,35 @@ protected: static const float FixedGrabberSize; static const float FixedRadiusSize; - Vec3d center; - Vec3d angles; - std::array color; - std::array hover_color; - bool enabled; - bool dragging; + bool enabled{ true }; + bool dragging{ false }; + Vec3d center{ Vec3d::Zero() }; + Vec3d angles{ Vec3d::Zero() }; + Transform3d matrix{ Transform3d::Identity() }; + ColorRGBA color{GRABBER_NORMAL_COL}; + ColorRGBA hover_color{GRABBER_HOVER_COL}; + EGrabberExtension extensions{ EGrabberExtension::None }; + // the picking id shared by all the elements + int picking_id{ -1 }; + std::array, GRABBER_ELEMENTS_MAX_COUNT> raycasters = { nullptr }; - Grabber(); + Grabber() = default; + ~Grabber(); - void render(bool hover, float size) const; - void render_for_picking(float size) const { render(size, color, true); } + void render(bool hover, float size) { render(size, hover ? hover_color : color); } float get_half_size(float size) const; float get_dragging_half_size(float size) const; - const GLModel& get_cube() const; + PickingModel &get_cube(); + + void register_raycasters_for_picking(int id); + void unregister_raycasters_for_picking(); private: - void render(float size, const std::array& render_color, bool picking) const; + void render(float size, const ColorRGBA& render_color); - GLModel cube; - bool cube_initialized = false; + static PickingModel s_cube; + static PickingModel s_cone; }; public: @@ -107,24 +136,17 @@ public: protected: GLCanvas3D& m_parent; - int m_group_id; + int m_group_id; // TODO: remove only for rotate EState m_state; int m_shortcut_key; std::string m_icon_filename; unsigned int m_sprite_id; - int m_hover_id; - bool m_dragging; - std::array m_base_color; - std::array m_drag_color; - std::array m_highlight_color; + int m_hover_id{ -1 }; + bool m_dragging{ false }; mutable std::vector m_grabbers; ImGuiWrapper* m_imgui; - bool m_first_input_window_render; - mutable std::string m_tooltip; - CommonGizmosDataPool* m_c; - GLModel m_cone; - GLModel m_cylinder; - GLModel m_sphere; + bool m_first_input_window_render{ true }; + CommonGizmosDataPool* m_c{ nullptr }; bool m_is_dark_mode = false; @@ -132,7 +154,7 @@ public: GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - virtual ~GLGizmoBase() {} + virtual ~GLGizmoBase() = default; bool init() { return on_init(); } @@ -141,9 +163,6 @@ public: std::string get_name(bool include_shortcut = true) const; - int get_group_id() const { return m_group_id; } - void set_group_id(int id) { m_group_id = id; } - EState get_state() const { return m_state; } void set_state(EState state) { m_state = state; on_set_state(); } @@ -159,7 +178,7 @@ public: virtual bool wants_enter_leave_snapshots() const { return false; } virtual std::string get_gizmo_entering_text() const { assert(false); return ""; } virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; } - virtual std::string get_action_snapshot_name() { return "Gizmo action"; } + virtual std::string get_action_snapshot_name() const; void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } virtual bool apply_clipping_plane() { return true; } @@ -168,32 +187,44 @@ public: int get_hover_id() const { return m_hover_id; } void set_hover_id(int id); - - void set_highlight_color(const std::array& color); - - void enable_grabber(unsigned int id); - void disable_grabber(unsigned int id); - - void start_dragging(); - void stop_dragging(); - + bool is_dragging() const { return m_dragging; } - void update(const UpdateData& data); - // returns True when Gizmo changed its state bool update_items_state(); - void render() { m_tooltip.clear(); on_render(); } - void render_for_picking() { on_render_for_picking(); } + void render() { on_render(); } void render_input_window(float x, float y, float bottom_limit); virtual void on_change_color_mode(bool is_dark) { m_is_dark_mode = is_dark; } + /// + /// Mouse tooltip text + /// + /// Text to be visible in mouse tooltip virtual std::string get_tooltip() const { return ""; } int get_count() { return ++count; } std::string get_gizmo_name() { return on_get_name(); } + /// + /// Is called when data (Selection) is changed + /// + virtual void data_changed(bool is_serializing){}; + + /// + /// Implement when want to process mouse events in gizmo + /// Click, Right click, move, drag, ... + /// + /// Keep information about mouse click + /// Return True when use the information and don't want to propagate it otherwise False. + virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; } + + void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); } + void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); } + + virtual bool is_in_editing_mode() const { return false; } + virtual bool is_selection_rectangle_dragging() const { return false; } + protected: float last_input_window_width = 0; virtual bool on_init() = 0; @@ -207,39 +238,51 @@ protected: virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); } virtual void on_enable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {} + + // called inside use_grabbers virtual void on_start_dragging() {} virtual void on_stop_dragging() {} - virtual void on_update(const UpdateData& data) {} + virtual void on_dragging(const UpdateData& data) {} + virtual void on_render() = 0; - virtual void on_render_for_picking() = 0; virtual void on_render_input_window(float x, float y, float bottom_limit) {} bool GizmoImguiBegin(const std::string& name, int flags); void GizmoImguiEnd(); void GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); - // Returns the picking color for the given id, based on the BASE_ID constant - // No check is made for clashing with other picking color (i.e. GLVolumes) - std::array picking_color_component(unsigned int id) const; + + void register_grabbers_for_picking(); + void unregister_grabbers_for_picking(); + virtual void on_register_raycasters_for_picking() {} + virtual void on_unregister_raycasters_for_picking() {} + void render_grabbers(const BoundingBoxf3& box) const; void render_grabbers(float size) const; - void render_grabbers_for_picking(const BoundingBoxf3& box) const; + void render_grabbers(size_t first, size_t last, float size, bool force_hover) const; std::string format(float value, unsigned int decimals) const; // Mark gizmo as dirty to Re-Render when idle() void set_dirty(); + + /// + /// function which + /// Set up m_dragging and call functions + /// on_start_dragging / on_dragging / on_stop_dragging + /// + /// Keep information about mouse click + /// same as on_mouse + bool use_grabbers(const wxMouseEvent &mouse_event); + + void do_stop_dragging(bool perform_mouse_cleanup); + private: // Flag for dirty visible state of Gizmo // When True then need new rendering - bool m_dirty; + bool m_dirty{ false }; int count = 0; }; - -// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components -// were not interpolated by alpha blending or multi sampling. -extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue); - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d5008725e4..c0267c9888 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1,343 +1,3838 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoCut.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include -#include -#include -#include -#include - #include #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp" +#include "slic3r/GUI/format.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/AppConfig.hpp" -#include "libslic3r/Model.hpp" #include "libslic3r/TriangleMeshSlicer.hpp" +#include "imgui/imgui_internal.h" +#include "slic3r/GUI/Field.hpp" +#include "slic3r/GUI/MsgDialog.hpp" + namespace Slic3r { namespace GUI { -const double GLGizmoCut::Offset = 10.0; -const double GLGizmoCut::Margin = 20.0; -const std::array GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0, 1.0 }; +static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); +static const ColorRGBA UPPER_PART_COLOR = ColorRGBA::CYAN(); +static const ColorRGBA LOWER_PART_COLOR = ColorRGBA::MAGENTA(); +static const ColorRGBA MODIFIER_COLOR = ColorRGBA(0.75f, 0.75f, 0.75f, 0.5f); -GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) +// connector colors +static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW(); +static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW(); +static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN(); +static const ColorRGBA HOVERED_DOWEL_COLOR = ColorRGBA(0.0f, 0.5f, 0.5f, 1.0f); +static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY(); +static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::DARK_GRAY(); +static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); +static const ColorRGBA CONNECTOR_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); +static const ColorRGBA HOVERED_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 1.0f); + +static const ColorRGBA CUT_PLANE_DEF_COLOR = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); +static const ColorRGBA CUT_PLANE_ERR_COLOR = ColorRGBA(1.0f, 0.8f, 0.8f, 0.5f); + +const unsigned int AngleResolution = 64; +const unsigned int ScaleStepsCount = 72; +const float ScaleStepRad = 2.0f * float(PI) / ScaleStepsCount; +const unsigned int ScaleLongEvery = 2; +const float ScaleLongTooth = 0.1f; // in percent of radius +const unsigned int SnapRegionsCount = 8; + +const float UndefFloat = -999.f; +const std::string UndefLabel = " "; + +using namespace Geometry; + +// Generates mesh for a line +static GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) +{ + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(beg_pos); + init_data.add_vertex(end_pos); + + // indices + init_data.add_line(0, 1); + return init_data; +} + +//! -- #ysFIXME those functions bodies are ported from GizmoRotation +// Generates mesh for a circle +static void init_from_circle(GLModel& model, double radius) +{ + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(ScaleStepsCount); + init_data.reserve_indices(ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + init_data.add_vertex(Vec3f(::cos(angle) * float(radius), ::sin(angle) * float(radius), 0.0f)); + init_data.add_index(i); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a scale +static void init_from_scale(GLModel& model, double radius) +{ + const float out_radius_long = float(radius) * (1.0f + ScaleLongTooth); + const float out_radius_short = float(radius) * (1.0f + 0.5f * ScaleLongTooth); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * float(radius); + const float in_y = sina * float(radius); + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a snap_radii +static void init_from_snap_radii(GLModel& model, double radius) +{ + const float step = 2.0f * float(PI) / float(SnapRegionsCount); + const float in_radius = float(radius) / 3.0f; + const float out_radius = 2.0f * in_radius; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i) * step; + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a angle_arc +static void init_from_angle_arc(GLModel& model, double angle, double radius) +{ + model.reset(); + + const float step_angle = float(angle) / float(AngleResolution); + const float ex_radius = float(radius); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(1 + AngleResolution); + init_data.reserve_indices(1 + AngleResolution); + + // vertices + indices + for (unsigned int i = 0; i <= AngleResolution; ++i) { + const float angle = float(i) * step_angle; + init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f)); + init_data.add_index(i); + } + + model.init_from(std::move(init_data)); +} + +//! -- + +GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} - -std::string GLGizmoCut::get_tooltip() const + , m_connectors_group_id (GrabberID::Count) + , m_connector_type (CutConnectorType::Plug) + , m_connector_style (int(CutConnectorStyle::Prism)) + , m_connector_shape_id (int(CutConnectorShape::Circle)) { - double cut_z = m_cut_z; - if (wxGetApp().app_config->get("use_inches") == "1") - cut_z *= ObjectManipulation::mm_to_in; + m_modes = { _u8L("Planar"), _u8L("Dovetail")//, _u8L("Grid") +// , _u8L("Radial"), _u8L("Modular") + }; - return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(cut_z, 2) : ""; + m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; + + m_connector_types = { _u8L("Plug"), _u8L("Dowel"), _u8L("Snap") }; + + m_connector_styles = { _u8L("Prism"), _u8L("Frustum") +// , _u8L("Claw") + }; + + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Hexagon"), _u8L("Circle") +// , _u8L("D-shape") + }; + + m_axis_names = { "X", "Y", "Z" }; + + m_part_orientation_names = { + {"none", _L("Keep orientation")}, + {"on_cut", _L("Place on cut")}, + {"flip", _L("Flip upside down")}, + }; + + m_labels_map = { + {"Connectors" , _u8L("Connectors")}, + {"Type" , _u8L("Type")}, + {"Style" , _u8L("Style")}, + {"Shape" , _u8L("Shape")}, + {"Depth" , _u8L("Depth")}, + {"Size" , _u8L("Size")}, + {"Rotation" , _u8L("Rotation")}, + {"Groove" , _u8L("Groove")}, + {"Width" , _u8L("Width")}, + {"Flap Angle" , _u8L("Flap Angle")}, + {"Groove Angle" , _u8L("Groove Angle")}, + }; + +// update_connector_shape(); } -bool GLGizmoCut::on_init() +std::string GLGizmoCut3D::get_tooltip() const { - m_grabbers.emplace_back(); - m_shortcut_key = WXK_CONTROL_C; - return true; + std::string tooltip; + if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) { + double koef = m_imperial_units ? GizmoObjectManipulation::mm_to_in : 1.0; + std::string unit_str = " " + (m_imperial_units ? _u8L("in") : _u8L("mm")); + const BoundingBoxf3& tbb = m_transformed_bounding_box; + + const std::string name = m_keep_as_parts ? _u8L("Part") : _u8L("Object"); + if (tbb.max.z() >= 0.0) { + double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; + tooltip += format(static_cast(top), 2) + " " + unit_str + " (" + name + " A)"; + if (tbb.min.z() <= 0.0) + tooltip += "\n"; + } + if (tbb.min.z() <= 0.0) { + double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef; + tooltip += format(static_cast(bottom), 2) + " " + unit_str + " (" + name + " B)"; + } + return tooltip; + } + + if (!m_dragging && m_hover_id == CutPlane) { + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return _u8L("Click to flip the cut plane\n" + "Drag to move the cut plane"); + return _u8L("Click to flip the cut plane\n" + "Drag to move the cut plane\n" + "Right-click a part to assign it to the other side"); + } + + if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation)) { + std::string axis = m_hover_id == X ? "X" : m_hover_id == Y ? "Y" : "Z"; + return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°"); + } + + return tooltip; } -std::string GLGizmoCut::on_get_name() const +bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { - return _u8L("Cut"); + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + + if (mouse_event.ShiftDown() && mouse_event.LeftDown()) + return gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + if (mouse_event.CmdDown() && mouse_event.LeftDown()) + return false; + if (cut_line_processing()) { + if (mouse_event.ShiftDown()) { + if (mouse_event.Moving()|| mouse_event.Dragging()) + return gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + if (mouse_event.LeftUp()) + return gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + } + discard_cut_line_processing(); + } + else if (mouse_event.Moving()) + return false; + + if (m_hover_id >= CutPlane && mouse_event.LeftDown() && !m_connectors_editing) { + // before processing of a use_grabbers(), detect start move position as a projection of mouse position to the cut plane + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_pos, pos, pos_world, false)) + m_cut_plane_start_move_pos = pos_world; + } + + if (use_grabbers(mouse_event)) { + if (m_hover_id >= m_connectors_group_id) { + if (mouse_event.LeftDown() && !mouse_event.CmdDown() && !mouse_event.AltDown()) + unselect_all_connectors(); + if (mouse_event.LeftUp() && !mouse_event.ShiftDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + } + else if (m_hover_id == CutPlane) { + if (mouse_event.LeftDown()) { + m_was_cut_plane_dragged = m_was_contour_selected = false; + + // disable / enable current contour + Vec3d pos; + Vec3d pos_world; + m_was_contour_selected = unproject_on_cut_plane(mouse_pos.cast(), pos, pos_world); + if (m_was_contour_selected) { + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + //m_c->object_clipper()->pass_mouse_click(pos_world); + //process_contours(); + return true; + } + + } + else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged && !m_was_contour_selected) + flip_cut_plane(); + } + + if (m_hover_id >= CutPlane && mouse_event.Dragging() && !m_connectors_editing) { + // if we continue to dragging a cut plane, than update a start move position as a projection of mouse position to the cut plane after processing of a use_grabbers() + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_pos, pos, pos_world, false)) + m_cut_plane_start_move_pos = pos_world; + } + + toggle_model_objects_visibility(); + return true; + } + + static bool pending_right_up = false; + if (mouse_event.LeftDown()) { + bool grabber_contains_mouse = (get_hover_id() != -1); + const bool shift_down = mouse_event.ShiftDown(); + if ((!shift_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + return true; + } + else if (mouse_event.Dragging()) { + bool control_down = mouse_event.CmdDown(); + if (m_parent.get_move_volume_id() != -1) { + // don't allow dragging objects with the Sla gizmo on + return true; + } + if (!control_down && + gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) { + // the gizmo got the event and took some action, no need to do + // anything more here + m_parent.set_as_dirty(); + return true; + } + if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) { + // CTRL has been pressed while already dragging -> stop current action + if (mouse_event.LeftIsDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); + else if (mouse_event.RightIsDown()) + pending_right_up = false; + } + } + else if (mouse_event.LeftUp() && !m_parent.is_mouse_dragging()) { + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event + // and stop processing - neither object moving or selecting is + // suppressed in that case + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } + else if (mouse_event.RightDown()) { + if (! m_connectors_editing && mouse_event.GetModifiers() == wxMOD_NONE && + CutMode(m_mode) == CutMode::cutPlanar) { + // Check the internal part raycasters. + if (! m_part_selection.valid()) + process_contours(); + m_part_selection.toggle_selection(mouse_pos); + check_and_update_connectors_state(); // after a contour is deactivated, its connectors are inside the object + return true; + } + + if (m_parent.get_selection().get_object_idx() != -1 && + gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) { + // we need to set the following right up as processed to avoid showing + // the context menu if the user release the mouse over the object + pending_right_up = true; + // event was taken care of by the SlaSupports gizmo + return true; + } + } + else if (pending_right_up && mouse_event.RightUp()) { + pending_right_up = false; + return true; + } + return false; } -void GLGizmoCut::on_set_state() +void GLGizmoCut3D::shift_cut(double delta) { - // Reset m_cut_z on gizmo activation - if (get_state() == On) - m_cut_z = bounding_box().center().z(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); + set_center(m_plane_center + m_cut_normal * delta, true); + m_ar_plane_center = m_plane_center; } -bool GLGizmoCut::on_is_activable() const +void GLGizmoCut3D::rotate_vec3d_around_plane_center(Vec3d&vec) { - const Selection& selection = m_parent.get_selection(); - return selection.is_single_full_instance() && !selection.is_wipe_tower(); + vec = Transformation(translation_transform(m_plane_center) * m_rotation_m * translation_transform(-m_plane_center)).get_matrix() * vec; } -void GLGizmoCut::on_start_dragging() +void GLGizmoCut3D::put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset) { - if (m_hover_id == -1) + ModelObject* mo = m_c->selection_info()->model_object(); + if (CutConnectors& connectors = mo->cut_connectors; !connectors.empty()) { + const float sla_shift = m_c->selection_info()->get_sla_shift(); + const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + + for (auto& connector : connectors) { + // convert connetor pos to the world coordinates + Vec3d pos = connector.pos + instance_offset; + pos[Z] += sla_shift; + // scalar distance from point to plane along the normal + double distance = -cp_normal.dot(pos) + cp_offset; + // move connector + connector.pos += distance * cp_normal; + } + } +} + +// returns true if the camera (forward) is pointing in the negative direction of the cut normal +bool GLGizmoCut3D::is_looking_forward() const +{ + const Camera& camera = wxGetApp().plater()->get_camera(); + const double dot = camera.get_dir_forward().dot(m_cut_normal); + return dot < 0.05; +} + +void GLGizmoCut3D::update_clipper() +{ + // update cut_normal + Vec3d normal = m_rotation_m * Vec3d::UnitZ(); + normal.normalize(); + m_cut_normal = normal; + + // calculate normal and offset for clipping plane + Vec3d beg = m_bb_center; + beg[Z] -= m_radius; + rotate_vec3d_around_plane_center(beg); + + m_clp_normal = normal; + double offset = normal.dot(m_plane_center); + double dist = normal.dot(beg); + + m_parent.set_color_clip_plane(normal, offset); + + if (!is_looking_forward()) { + // recalculate normal and offset for clipping plane, if camera is looking downward to cut plane + normal = m_rotation_m * (-1. * Vec3d::UnitZ()); + normal.normalize(); + + beg = m_bb_center; + beg[Z] += m_radius; + rotate_vec3d_around_plane_center(beg); + + m_clp_normal = normal; + offset = normal.dot(m_plane_center); + dist = normal.dot(beg); + } + + m_c->object_clipper()->set_range_and_pos(normal, offset, dist); + + put_connectors_on_cut_plane(normal, offset); + + if (m_raycasters.empty()) + on_register_raycasters_for_picking(); + else + update_raycasters_for_picking_transform(); +} + +void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) +{ + set_center_pos(center, update_tbb); + check_and_update_connectors_state(); + update_clipper(); +} + +void GLGizmoCut3D::switch_to_mode(size_t new_mode) +{ + m_mode = new_mode; + update_raycasters_for_picking(); + + apply_color_clip_plane_colors(); + if (auto oc = m_c->object_clipper()) { + m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + } + + update_plane_model(); + reset_cut_by_contours(); +} + +bool GLGizmoCut3D::render_cut_mode_combo() +{ + ImGui::AlignTextToFramePadding(); + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + int selection_idx = int(m_mode); + const bool is_changed = m_imgui->combo(_u8L("Mode"), m_modes, selection_idx, 0, m_label_width, m_control_width); + ImGuiWrapper::pop_combo_style(); + + if (is_changed) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Change cut mode"), UndoRedo::SnapshotType::GizmoAction); + switch_to_mode(size_t(selection_idx)); + check_and_update_connectors_state(); + } + + return is_changed; +} + +bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, int& selection_idx) +{ + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_editing_window_width); + + size_t selection_out = selection_idx; + + const char* selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : ""; + if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) { + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ImGui::PushID(int(line_idx)); + if (ImGui::Selectable("", line_idx == selection_idx)) + selection_out = line_idx; + + ImGui::SameLine(); + ImGui::Text("%s", lines[line_idx].c_str()); + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + bool is_changed = selection_idx != selection_out; + selection_idx = selection_out; + + //if (is_changed) update_connector_shape(); + ImGuiWrapper::pop_combo_style(); + + return is_changed; +} + +bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in) +{ + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width); + + double value = value_in; + if (m_imperial_units) + value *= GizmoObjectManipulation::mm_to_in; + double old_val = value; + ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + + ImGui::SameLine(); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + + value_in = value * (m_imperial_units ? GizmoObjectManipulation::in_to_mm : 1.0); + return !is_approx(old_val, value); +} + +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) +{ + // -------- [ ] -------- [ ] + // slider_with + item_in_gap + first_input_width + item_out_gap + slider_with + item_in_gap + second_input_width + double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35; + double item_in_gap = 0.01 * m_editing_window_width; + double item_out_gap = 0.01 * m_editing_window_width; + double first_input_width = 0.29 * m_editing_window_width; + double second_input_width = 0.29 * m_editing_window_width; + + constexpr float UndefMinVal = -0.1f; + const float f_mm_to_in = static_cast(GizmoObjectManipulation::mm_to_in); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(slider_with); + + double left_width = m_label_width + slider_with + item_in_gap; + + bool m_imperial_units = false; + + float value = value_in; + if (m_imperial_units) + value *= f_mm_to_in; + float old_val = value; + + const BoundingBoxf3 bbox = m_bounding_box; + const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f); + const float min_v = min_val > 0.f ? /*std::min(max_val, mean_size)*/min_val : 1.f; + + float min_size = value_in < 0.f ? UndefMinVal : min_v; + if (m_imperial_units) { + min_size *= f_mm_to_in; + } + std::string format = value_in < 0.f ? " " : m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); + + m_imgui->bbl_slider_float_style(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); + + ImGui::SameLine(left_width); + ImGui::PushItemWidth(first_input_width); + ImGui::BBLDragFloat(("##input_" + label).c_str(), &value, 0.05f, min_size, mean_size, format.c_str()); + + value_in = value * float(m_imperial_units ? GizmoObjectManipulation::in_to_mm : 1.0); + + left_width += (first_input_width + item_out_gap); + ImGui::SameLine(left_width); + ImGui::PushItemWidth(slider_with); + + float tolerance = tolerance_in; + if (m_imperial_units) + tolerance *= f_mm_to_in; + float old_tolerance = tolerance; + // std::string format_t = tolerance_in < 0.f ? " " : "%.f %%"; + float min_tolerance = tolerance_in < 0.f ? UndefMinVal : 0.f; + const float max_tolerance_v = max_tolerance > 0.f ? std::min(max_tolerance, 0.5f * mean_size) : 0.5f * mean_size; + + m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, max_tolerance_v, format.c_str(), 1.f, true, + _L("Tolerance")); + + left_width += (slider_with + item_in_gap); + ImGui::SameLine(left_width); + ImGui::PushItemWidth(second_input_width); + ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, max_tolerance_v, format.c_str()); + + tolerance_in = tolerance * float(m_imperial_units ? GizmoObjectManipulation::in_to_mm : 1.0); + + return !is_approx(old_val, value) || !is_approx(old_tolerance, tolerance); +} + +void GLGizmoCut3D::render_move_center_input(int axis) +{ + m_imgui->text(m_axis_names[axis]+":"); + ImGui::SameLine(); + ImGui::PushItemWidth(0.3f*m_control_width); + + Vec3d move = m_plane_center; + double in_val, value = in_val = move[axis]; + if (m_imperial_units) + value *= GizmoObjectManipulation::mm_to_in; + ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0, 0.0, "%.2f", ImGuiInputTextFlags_CharsDecimal); + ImGui::SameLine(); + + double val = value * (m_imperial_units ? GizmoObjectManipulation::in_to_mm : 1.0); + + if (in_val != val) { + move[axis] = val; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); + set_center(move, true); + m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); + } +} + +bool GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type) +{ + ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 0); + ImGui::PushItemWidth(m_control_width); + if (ImGui::RadioButton(m_connector_types[size_t(type)].c_str(), m_connector_type == type)) { + m_connector_type = type; +// update_connector_shape(); + return true; + } + return false; +} + +void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) +{ + ImGui::SameLine(mode == CutConnectorMode::Auto ? m_label_width : 2 * m_label_width); + ImGui::PushItemWidth(m_control_width); + if (ImGui::RadioButton(m_connector_modes[int(mode)].c_str(), m_connector_mode == mode)) + m_connector_mode = mode; +} + +bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::string& tooltip) const +{ + const ImGuiStyle &style = ImGui::GetStyle(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {1, style.ItemSpacing.y}); + + ImGui::PushStyleColor(ImGuiCol_Button, {0.25f, 0.25f, 0.25f, 0.0f}); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, {0.4f, 0.4f, 0.4f, 1.0f}); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0.4f, 0.4f, 0.4f, 1.0f}); + + const bool revert = m_imgui->button(wxString(ImGui::RevertBtn) + "##" + label_id); + + ImGui::PopStyleColor(3); + + if (ImGui::IsItemHovered()) + m_imgui->tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f); + + ImGui::PopStyleVar(); + + return revert; +} + +static double get_grabber_mean_size(const BoundingBoxf3& bb) +{ + return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; +} + +indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() +{ + // values for calculation + + const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); + + const float groove_half_width_upper = 0.5f * (m_groove.width); + const float groove_half_width_lower = 0.5f * (m_groove.width + flaps_width); + + const float cut_plane_radius = 1.5f * float(m_radius); + const float cut_plane_length = 1.5f * cut_plane_radius; + + const float groove_half_depth = 0.5f * m_groove.depth; + + const float x = 0.5f * cut_plane_radius; + const float y = 0.5f * cut_plane_length; + float z_upper = groove_half_depth; + float z_lower = -groove_half_depth; + + const float proj = y * tan(m_groove.angle); + + float ext_upper_x = groove_half_width_upper + proj; // upper_x extension + float ext_lower_x = groove_half_width_lower + proj; // lower_x extension + + float nar_upper_x = groove_half_width_upper - proj; // upper_x narrowing + float nar_lower_x = groove_half_width_lower - proj; // lower_x narrowing + + const float cut_plane_thiknes = 0.02f;// 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut_plane_thiknes + + // Vertices of the groove used to detection if groove is valid + // They are written as: + // {left_ext_lower, left_nar_lower, left_ext_upper, left_nar_upper, + // right_ext_lower, right_nar_lower, right_ext_upper, right_nar_upper } + { + m_groove_vertices.clear(); + m_groove_vertices.reserve(8); + + m_groove_vertices.emplace_back(Vec3f(-ext_lower_x, -y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f(-nar_lower_x, y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f(-ext_upper_x, -y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f(-nar_upper_x, y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f( ext_lower_x, -y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f( nar_lower_x, y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f( ext_upper_x, -y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f( nar_upper_x, y, z_upper).cast()); + } + + // Different cases of groove plane: + + // groove is open + + if (groove_half_width_upper > proj && groove_half_width_lower > proj) { + indexed_triangle_set mesh; + + auto get_vertices = [x, y](float z_upper, float z_lower, float nar_upper_x, float nar_lower_x, float ext_upper_x, float ext_lower_x) { + return std::vector({ + // upper left part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {-nar_upper_x, y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower}, + // upper right part vertices + {ext_upper_x, -y, z_upper}, {nar_upper_x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper} + }); + }; + + mesh.vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.reserve(2 * mesh.vertices.size()); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); + + nar_upper_x += under_x_shift; + nar_lower_x += under_x_shift; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {5,4,7}, {5,7,6}, // lower part + {3,4,5}, {3,5,2}, // left side + {9,6,8}, {8,6,7}, // right side + {1,0,2}, {2,0,3}, // upper left part + {9,8,10}, {10,8,11}, // upper right part + // under view + {20,21,22}, {20,22,23}, // upper right part + {12,13,14}, {12,14,15}, // upper left part + {18,21,20}, {18,20,19}, // right side + {16,15,14}, {16,14,17}, // left side + {16,17,18}, {16,18,19}, // lower part + // left edge + {1,13,0}, {0,13,12}, + // front edge + {0,12,3}, {3,12,15}, {3,15,4}, {4,15,16}, {4,16,7}, {7,16,19}, {7,19,20}, {7,20,8}, {8,20,11}, {11,20,23}, + // right edge + {11,23,10}, {10,23,22}, + // back edge + {1,13,2}, {2,13,14}, {2,14,17}, {2,17,5}, {5,17,6}, {6,17,18}, {6,18,9}, {9,18,21}, {9,21,10}, {10,21,22} + }; + return mesh; + } + + float cross_pt_upper_y = groove_half_width_upper / tan(m_groove.angle); + + // groove is closed + + if (groove_half_width_upper < proj && groove_half_width_lower < proj) { + float cross_pt_lower_y = groove_half_width_lower / tan(m_groove.angle); + + indexed_triangle_set mesh; + + auto get_vertices = [x, y](float z_upper, float z_lower, float cross_pt_upper_y, float cross_pt_lower_y, float ext_upper_x, float ext_lower_x) { + return std::vector({ + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {0.f, cross_pt_lower_y, z_lower}, {ext_lower_x, -y, z_lower} + }); + }; + + mesh.vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.reserve(2 * mesh.vertices.size()); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); + + cross_pt_upper_y += cut_plane_thiknes; + cross_pt_lower_y += cut_plane_thiknes; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {8,7,9}, // lower part + {5,8,6}, {6,8,7}, // left side + {4,9,8}, {4,8,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {10,11,16}, {16,11,15}, {15,11,12}, {15,12,14}, {14,12,13}, // upper part + {18,15,14}, {14,18,19}, // right side + {17,16,15}, {17,15,18}, // left side + {17,18,19}, // lower part + // left edge + {1,11,0}, {0,11,10}, + // front edge + {0,10,6}, {6,10,16}, {6,17,16}, {6,7,17}, {7,17,19}, {7,19,9}, {4,14,19}, {4,19,9}, {4,14,13}, {4,13,3}, + // right edge + {3,13,12}, {3,12,2}, + // back edge + {2,12,11}, {2,11,1} + }; + + return mesh; + } + + // groove is closed from the roof + + indexed_triangle_set mesh; + mesh.vertices = { + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower} + }; + + mesh.vertices.reserve(2 * mesh.vertices.size() + 1); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); + + nar_lower_x += under_x_shift; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = { + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {under_x_shift, cross_pt_upper_y, z_upper}, {-under_x_shift, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower} + }; + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {8,7,10}, {8,10,9}, // lower part + {5,8,7}, {5,7,6}, // left side + {4,10,9}, {4,9,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {11,12,18}, {18,12,17}, {17,12,16}, {16,12,13}, {16,13,15}, {15,13,14}, // upper part + {21,16,15}, {21,15,22}, // right side + {19,18,17}, {19,17,20}, // left side + {19,20,21}, {19,21,22}, // lower part + // left edge + {1,12,11}, {1,11,0}, + // front edge + {0,11,18}, {0,18,6}, {7,19,18}, {7,18,6}, {7,19,22}, {7,22,10}, {10,22,15}, {10,15,4}, {4,15,14}, {4,14,3}, + // right edge + {3,14,13}, {3,14,2}, + // back edge + {2,13,12}, {2,12,1}, {5,16,21}, {5,21,9}, {9,21,20}, {9,20,8}, {5,17,20}, {5,20,8} + }; + + return mesh; +} + +void GLGizmoCut3D::render_cut_plane() +{ + if (cut_line_processing()) return; - const BoundingBoxf3 box = bounding_box(); - m_max_z = box.max.z(); - m_start_z = m_cut_z; - m_drag_pos = m_grabbers[m_hover_id].center; - m_drag_center = box.center(); - m_drag_center.z() = m_cut_z; -} + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; -void GLGizmoCut::on_update(const UpdateData& data) -{ - if (m_hover_id != -1) - set_cut_z(m_start_z + calc_projection(data.mouse_ray)); -} - -void GLGizmoCut::on_render() -{ - const BoundingBoxf3 box = bounding_box(); - Vec3d plane_center = box.center(); - plane_center.z() = m_cut_z; - m_max_z = box.max.z(); - set_cut_z(m_cut_z); - - update_contours(); - - const float min_x = box.min.x() - Margin; - const float max_x = box.max.x() + Margin; - const float min_y = box.min.y() - Margin; - const float max_y = box.max.y() + Margin; glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - // Draw the cutting plane - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, plane_center.z()); - ::glVertex3f(max_x, min_y, plane_center.z()); - ::glVertex3f(max_x, max_y, plane_center.z()); - ::glVertex3f(min_x, max_y, plane_center.z()); - glsafe(::glEnd()); + shader->start_using(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + ColorRGBA cp_clr = can_perform_cut() && has_valid_groove() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + cp_clr.a(cp_clr.a() - 0.1f); + m_plane.model.set_color(cp_clr); + + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + shader->set_uniform("view_model_matrix", view_model_matrix); + m_plane.model.render(); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); - // TODO: draw cut part contour? + shader->stop_using(); +} - // Draw the grabber and the connecting line - m_grabbers[0].center = plane_center; - m_grabbers[0].center.z() = plane_center.z() + Offset; +static double get_half_size(double size) +{ + return std::max(size * 0.35, 0.05); +} +static double get_dragging_half_size(double size) +{ + return get_half_size(size) * 1.25; +} + +void GLGizmoCut3D::render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader) { + shader->start_using(); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("emission_factor", 0.2f); + shader->set_uniform("projection_matrix", wxGetApp().plater()->get_camera().get_projection_matrix()); + + model.set_color(color); + model.render(); + + shader->stop_using(); + } +} + +void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width) +{ + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader) { + shader->start_using(); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", wxGetApp().plater()->get_camera().get_projection_matrix()); + shader->set_uniform("width", width); + + line_model.set_color(color); + line_model.render(); + + shader->stop_using(); + } +} + +void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& color) +{ + GLShaderProgram* line_shader = wxGetApp().get_shader("flat"); + if (!line_shader) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_start_dragging_m; + + if (axis == X) + view_model_matrix = view_model_matrix * rotation_transform(0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); + else if (axis == Y) + view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * rotation_transform(-0.5 * PI * Vec3d::UnitY()); + else + view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()); + + line_shader->start_using(); + line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + line_shader->set_uniform("view_model_matrix", view_model_matrix); + line_shader->set_uniform("width", 0.25f); + + m_circle.render(); + m_scale.render(); + m_snap_radii.render(); + m_reference_radius.render(); + if (m_dragging) { + line_shader->set_uniform("width", 1.5f); + m_angle_arc.set_color(color); + m_angle_arc.render(); + } + + line_shader->stop_using(); +} + +void GLGizmoCut3D::render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef/* = 1.0*/) +{ + const Transform3d line_view_matrix = view_matrix * scale_transform(Vec3d(1.0, 1.0, line_len_koef * m_grabber_connection_len)); + + render_line(m_grabber_connection, color, line_view_matrix, 0.2f); +}; + +void GLGizmoCut3D::render_cut_plane_grabbers() +{ glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); - glsafe(::glColor3f(1.0, 1.0, 0.0)); - ::glBegin(GL_LINES); - ::glVertex3dv(plane_center.data()); - ::glVertex3dv(m_grabbers[0].center.data()); - glsafe(::glEnd()); + ColorRGBA color = ColorRGBA::GRAY(); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); + const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - m_grabbers[0].color = GrabberColor; - m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); + const double mean_size = get_grabber_mean_size(m_bounding_box); + double size; - shader->stop_using(); + const bool no_xy_dragging = m_dragging && m_hover_id == CutPlane; - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); -} + if (!no_xy_dragging && m_hover_id != CutPlaneZRotation && m_hover_id != CutPlaneXMove && m_hover_id != CutPlaneYMove) { + render_grabber_connection(GRABBER_COLOR, view_matrix); -void GLGizmoCut::on_render_for_picking() -{ - glsafe(::glDisable(GL_DEPTH_TEST)); - render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); -} - -void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) -{ - //static float last_y = 0.0f; - //static float last_h = 0.0f; - - //BBS: GUI refactor: move gizmo to the right -#if BBS_TOOLBAR_ON_TOP - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always, 0.5f, 0.0f); -#else - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always, 1.0f, 0.0f); -#endif - - //BBS - ImGuiWrapper::push_toolbar_style(); - - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - - // adjust window position to avoid overlap the view toolbar - /*const float win_h = ImGui::GetWindowHeight(); - y = std::min(y, bottom_limit - win_h); - ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - if (last_h != win_h || last_y != y) { - // ask canvas for another frame to render the window in the correct position -#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT - m_imgui->set_requires_extra_frame(); -#else - m_parent.request_extra_frame(); -#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT - if (last_h != win_h) - last_h = win_h; - if (last_y != y) - last_y = y; - }*/ - - ImGui::AlignTextToFramePadding(); - m_imgui->text("Z"); - ImGui::SameLine(); - ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f); - - double cut_z = m_cut_z; - if (imperial_units) - cut_z *= ObjectManipulation::mm_to_in; - ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); - - ImGui::SameLine(); - m_imgui->text(imperial_units ? _L("in") : _L("mm")); - - m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0); - - ImGui::Separator(); - - m_imgui->checkbox(_L("Keep upper part"), m_keep_upper); - m_imgui->checkbox(_L("Keep lower part"), m_keep_lower); - m_imgui->checkbox(_L("Cut to parts"), m_cut_to_parts); // BBS - m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); - - // BBS - ImGui::Separator(); - m_imgui->checkbox(_L("Auto Segment"), m_do_segment); - m_imgui->disabled_begin(!m_do_segment); - ImGui::InputDouble("smoothing_alpha", &m_segment_smoothing_alpha, 0.0f, 0.0f, "%.2f"); - m_segment_smoothing_alpha = std::max(0.1, std::min(100.0, m_segment_smoothing_alpha)); - ImGui::InputInt("segment number", &m_segment_number); - m_segment_number = std::max(1, m_segment_number); - m_imgui->disabled_end(); - - ImGui::Separator(); - - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower && !m_do_segment) || m_cut_z <= 0.0 || m_max_z <= m_cut_z); - const bool cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); - - m_imgui->end(); - - //BBS - ImGuiWrapper::pop_toolbar_style(); - - // BBS: m_do_segment - if (cut_clicked && (m_keep_upper || m_keep_lower || m_do_segment)) - perform_cut(m_parent.get_selection()); -} - -void GLGizmoCut::set_cut_z(double cut_z) -{ - // Clamp the plane to the object's bounding box - m_cut_z = std::clamp(cut_z, 0.0, m_max_z); -} - -void GLGizmoCut::perform_cut(const Selection& selection) -{ - const int instance_idx = selection.get_instance_idx(); - const int object_idx = selection.get_object_idx(); - - wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); - - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const double object_cut_z = m_cut_z - first_glvolume->get_sla_shift_z(); - - // BBS: do segment - if (m_do_segment) - { - wxGetApp().plater()->segment(object_idx, instance_idx, m_segment_smoothing_alpha, m_segment_number); + // render sphere grabber + size = m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size); + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : + m_hover_id == X ? complementary(ColorRGBA::RED()) : + m_hover_id == Z ? GRABBER_COLOR : ColorRGBA::GRAY(); + render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); } - else if (0.0 < object_cut_z && object_cut_z < m_max_z) { - //wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower, m_cut_to_parts); + + const bool no_xy_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane); + + // render X grabber + + if (no_xy_grabber_hovered || m_hover_id == X) + { + size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size); + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); + + if (m_hover_id == X) { + render_grabber_connection(color, view_matrix); + render_rotation_snapping(X, color); + } + + Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + } + + // render Y grabber + + if (no_xy_grabber_hovered || m_hover_id == Y) + { + size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size); + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); + + if (m_hover_id == Y) { + render_grabber_connection(color, view_matrix); + render_rotation_snapping(Y, color); + } + + Vec3d offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + + // render CutPlaneZRotation grabber + + if (no_xy_grabber_hovered || m_hover_id == CutPlaneZRotation) + { + size = 0.75 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = ColorRGBA::BLUE(); + const ColorRGBA cp_color = m_hover_id == CutPlaneZRotation ? color : m_plane.model.get_color(); + + const double grabber_shift = -1.75 * m_grabber_connection_len; + + render_model(m_sphere.model, cp_color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + + if (m_hover_id == CutPlaneZRotation) { + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + render_rotation_snapping(CutPlaneZRotation, color); + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitX()), 1.75); + + Vec3d offset = Vec3d(1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } + } + + const double xy_connection_len = 0.75 * m_grabber_connection_len; + + // render CutPlaneXMove grabber + + if (no_xy_grabber_hovered || m_hover_id == CutPlaneXMove) + { + size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = m_hover_id == CutPlaneXMove ? ColorRGBA::RED() : m_plane.model.get_color(); + + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitY()), 0.75); + + Vec3d offset = xy_connection_len * Vec3d::UnitX() - 0.5 * size * Vec3d::Ones(); + render_model(m_cube.model, color, view_matrix * translation_transform(offset) * scale_transform(size)); + + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); + + offset = (size + xy_connection_len) * Vec3d::UnitX(); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } + + // render CutPlaneYMove grabber + + if (m_groove.angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) + { + size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color(); + + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitX()), 0.75); + + Vec3d offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); + render_model(m_cube.model, color, view_matrix * translation_transform(offset) * scale_transform(size)); + + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); + + offset = (size + xy_connection_len) * Vec3d::UnitY(); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + } + } +} + +void GLGizmoCut3D::render_cut_line() +{ + if (!cut_line_processing() || m_line_end.isApprox(Vec3d::Zero())) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + m_cut_line.reset(); + m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); + + render_line(m_cut_line, GRABBER_COLOR, wxGetApp().plater()->get_camera().get_view_matrix(), 0.25f); +} + +bool GLGizmoCut3D::on_init() +{ + m_grabbers.emplace_back(); + m_shortcut_key = WXK_CONTROL_C; + + // initiate info shortcuts + const wxString ctrl = GUI::shortkey_ctrl_prefix(); + const wxString alt = GUI::shortkey_alt_prefix(); + const wxString shift = "Shift+"; + + m_shortcuts_cut.push_back(std::make_pair(shift + _L("Drag"), _L("Draw cut line"))); + + m_shortcuts_connector.push_back(std::make_pair(_L("Left click"), _L("Add connector"))); + m_shortcuts_connector.push_back(std::make_pair(_L("Right click"), _L("Remove connector"))); + m_shortcuts_connector.push_back(std::make_pair(_L("Drag"), _L("Move connector"))); + m_shortcuts_connector.push_back(std::make_pair(shift + _L("Left click"), _L("Add connector to selection"))); + m_shortcuts_connector.push_back(std::make_pair(alt + _L("Left click"), _L("Remove connector from selection"))); + m_shortcuts_connector.push_back(std::make_pair(ctrl + "A", _L("Select all connectors"))); + + return true; +} + +void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) +{ + size_t mode; + float groove_depth; + float groove_width; + float groove_flaps_angle; + float groove_angle; + float groove_depth_tolerance; + float groove_width_tolerance; + + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, mode, m_connectors_editing, + m_ar_plane_center, m_rotation_m, + groove_depth, groove_width, groove_flaps_angle, groove_angle, groove_depth_tolerance, groove_width_tolerance); + + m_start_dragging_m = m_rotation_m; + + m_transformed_bounding_box = transformed_bounding_box(m_ar_plane_center, m_rotation_m); + set_center_pos(m_ar_plane_center); + + if (m_mode != mode) + switch_to_mode(mode); + else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + if (!is_approx(m_groove.depth , groove_depth) || + !is_approx(m_groove.width , groove_width) || + !is_approx(m_groove.flaps_angle , groove_flaps_angle) || + !is_approx(m_groove.angle , groove_angle) || + !is_approx(m_groove.depth_tolerance, groove_depth_tolerance) || + !is_approx(m_groove.width_tolerance, groove_width_tolerance) ) + { + m_groove.depth = groove_depth; + m_groove.width = groove_width; + m_groove.flaps_angle = groove_flaps_angle; + m_groove.angle = groove_angle; + m_groove.depth_tolerance= groove_depth_tolerance; + m_groove.width_tolerance= groove_width_tolerance; + update_plane_model(); + } + reset_cut_by_contours(); + } + + m_parent.request_extra_frame(); +} + +void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const +{ + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing, + m_ar_plane_center, m_start_dragging_m, + m_groove.depth, m_groove.width, m_groove.flaps_angle, m_groove.angle, m_groove.depth_tolerance, m_groove.width_tolerance); +} + +std::string GLGizmoCut3D::on_get_name() const +{ + return _u8L("Cut"); +} + +void GLGizmoCut3D::apply_color_clip_plane_colors() +{ + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + m_parent.set_color_clip_plane_colors({ CUT_PLANE_DEF_COLOR , CUT_PLANE_DEF_COLOR }); + else + m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR }); +} + +void GLGizmoCut3D::on_set_state() +{ + if (m_state == On) { + m_parent.set_use_color_clip_plane(true); + + update_bb(); + m_connectors_editing = !m_selected.empty(); + m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m); + + // initiate archived values + m_ar_plane_center = m_plane_center; + m_start_dragging_m = m_rotation_m; + reset_cut_by_contours(); + + m_parent.request_extra_frame(); } else { - // the object is SLA-elevated and the plane is under it. + if (auto oc = m_c->object_clipper()) { + oc->set_behavior(true, true, 0.); + oc->release(); + } + m_selected.clear(); + m_parent.set_use_color_clip_plane(false); + //m_c->selection_info()->set_use_shift(false); + + // Make sure that the part selection data are released when the gizmo is closed. + // The CallAfter is needed because in perform_cut, the gizmo is closed BEFORE + // the cut is performed (because of undo/redo snapshots), so the data would + // be deleted prematurely. + if (m_part_selection.valid()) + wxGetApp().CallAfter([this]() { m_part_selection = PartSelection(); }); } } -double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const +void GLGizmoCut3D::on_register_raycasters_for_picking() { - double projection = 0.0; + // assert(m_raycasters.empty()); + if (!m_raycasters.empty()) + on_unregister_raycasters_for_picking(); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); - const Vec3d starting_vec = m_drag_pos - m_drag_center; - const double len_starting_vec = starting_vec.norm(); - if (len_starting_vec != 0.0) { - const Vec3d mouse_dir = mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + init_picking_models(); + + if (m_connectors_editing) { + if (CommonGizmosDataObjects::SelectionInfo* si = m_c->selection_info()) { + const CutConnectors& connectors = si->model_object()->cut_connectors; + for (int i = 0; i < int(connectors.size()); ++i) + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i + m_connectors_group_id, *(m_shapes[connectors[i].attribs]).mesh_raycaster, Transform3d::Identity())); + } + } + else if (!cut_line_processing()) { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, X, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, X, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::FallbackGizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_sphere.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cube.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cube.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + } + } + + update_raycasters_for_picking_transform(); +} + +void GLGizmoCut3D::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::FallbackGizmo); + m_raycasters.clear(); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(false); +} + +void GLGizmoCut3D::update_raycasters_for_picking() +{ + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +} + +void GLGizmoCut3D::set_volumes_picking_state(bool state) +{ + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (raycasters != nullptr) { + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(state); + } + } +} + +void GLGizmoCut3D::update_raycasters_for_picking_transform() +{ + if (m_connectors_editing) { + CommonGizmosDataObjects::SelectionInfo* si = m_c->selection_info(); + if (!si) + return; + const ModelObject* mo = si->model_object(); + const CutConnectors& connectors = mo->cut_connectors; + if (connectors.empty()) + return; + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + + const Vec3d& instance_offset = mo->instances[inst_id]->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + const bool looking_forward = is_looking_forward(); + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector& connector = connectors[i]; + + float height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset; + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prism) { + height = 0.05f; + if (!looking_forward) + pos += 0.05 * m_clp_normal; + } + pos[Z] += sla_shift; + + const Transform3d scale_trafo = scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); + m_raycasters[i]->set_transform(translation_transform(pos) * m_rotation_m * scale_trafo); + } + } + else if (!cut_line_processing()){ + const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m; + + const BoundingBoxf3 box = m_bounding_box; + + const double size = get_half_size(get_grabber_mean_size(box)); + Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + int id = 0; + + Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + + offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + + m_raycasters[id++]->set_transform(trafo * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); + + m_raycasters[id++]->set_transform(trafo); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + + double grabber_y_shift = -1.75 * m_grabber_connection_len; + + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_y_shift * Vec3d::UnitY()) * scale_transform(size)); + + offset = Vec3d(1.25 * size, grabber_y_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + offset = Vec3d(-1.25 * size, grabber_y_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + + const double xy_connection_len = 0.75 * m_grabber_connection_len; + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); + + offset = xy_connection_len * Vec3d::UnitX() - 0.5 * size * Vec3d::Ones(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); + offset = (size + xy_connection_len) * Vec3d::UnitX(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + + if (m_groove.angle > 0.0f) { + offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); + offset = (size + xy_connection_len) * Vec3d::UnitY(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + } + else { + // discard transformation for CutPlaneYMove grabbers + m_raycasters[id++]->set_transform(Transform3d::Identity()); + m_raycasters[id++]->set_transform(Transform3d::Identity()); + } + } + } +} + +void GLGizmoCut3D::update_plane_model() +{ + m_plane.reset(); + on_unregister_raycasters_for_picking(); + + init_picking_models(); +} + +void GLGizmoCut3D::on_set_hover_id() +{ +} + +bool GLGizmoCut3D::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + const int object_idx = selection.get_object_idx(); + if (object_idx < 0 || selection.is_wipe_tower()) + return false; + + if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; + mo->is_cut() && mo->volumes.size() == 1) { + const ModelVolume* volume = mo->volumes[0]; + if (volume->is_cut_connector() && volume->cut_info.connector_type == CutConnectorType::Dowel) + return false; + } + + // This is assumed in GLCanvas3D::do_rotate, do not change this + // without updating that function too. + return selection.is_single_full_instance() && !m_parent.is_layers_editing_enabled(); +} + +bool GLGizmoCut3D::on_is_selectable() const +{ + return wxGetApp().get_mode() != comSimple; +} + +Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& mouse_ray) const +{ + double half_pi = 0.5 * PI; + + Transform3d m = Transform3d::Identity(); + + switch (axis) + { + case X: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); + break; + } + case Y: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY())); + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + break; + } + case Z: + default: + { + // no rotation applied + break; + } + } + + m = m * m_start_dragging_m.inverse(); + m.translate(-m_plane_center); + + return transform(mouse_ray, m).intersect_plane(0.0); +} + +void GLGizmoCut3D::dragging_grabber_move(const GLGizmoBase::UpdateData &data) +{ + Vec3d starting_drag_position; + if (m_hover_id == Z) + starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * (m_grabber_connection_len * Vec3d::UnitZ()); + else + starting_drag_position = m_cut_plane_start_move_pos; + + double projection = 0.0; + + Vec3d starting_vec = m_rotation_m * (m_hover_id == CutPlaneXMove ? Vec3d::UnitX() : m_hover_id == CutPlaneYMove ? Vec3d::UnitY() : Vec3d::UnitZ()); + if (starting_vec.norm() != 0.0) { + const Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing through the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebraic form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - const Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection - const Vec3d inters_vec = inters - m_drag_pos; + const Vec3d inters_vec = inters - starting_drag_position; + starting_vec.normalize(); // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec.normalized()); + projection = inters_vec.dot(starting_vec); } - return projection; + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * std::round(projection / m_snap_step); + + const Vec3d shift = starting_vec * projection; + if (shift != Vec3d::Zero()) + reset_cut_by_contours(); + + // move cut plane center + set_center(m_plane_center + shift, true); + + m_was_cut_plane_dragged = true; } -BoundingBoxf3 GLGizmoCut::bounding_box() const +void GLGizmoCut3D::dragging_grabber_rotation(const GLGizmoBase::UpdateData &data) +{ + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((GrabberID)m_hover_id, data.mouse_ray)); + + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); + + const double two_pi = 2.0 * PI; + + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); + if (cross2(orig_dir, new_dir) < 0.0) + theta = two_pi - theta; + + const double len = mouse_pos.norm(); + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = two_pi / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + // snap to fine snap region (scale) + else if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = two_pi / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + + if (is_approx(theta, two_pi)) + theta = 0.0; + if (m_hover_id != Y) + theta += 0.5 * PI; + + if (!is_approx(theta, 0.0)) + reset_cut_by_contours(); + + Vec3d rotation = Vec3d::Zero(); + rotation[m_hover_id == CutPlaneZRotation ? Z : m_hover_id] = theta; + + const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); + const bool update_tbb = !m_rotation_m.rotation().isApprox(rotation_tmp.rotation()); + m_rotation_m = rotation_tmp; + if (update_tbb) + m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m); + + m_angle = theta; + while (m_angle > two_pi) + m_angle -= two_pi; + if (m_angle < 0.0) + m_angle += two_pi; + + update_clipper(); +} + +void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data) +{ + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + Vec3d pos; + Vec3d pos_world; + + if (unproject_on_cut_plane(data.mouse_pos.cast(), pos, pos_world)) { + connectors[m_hover_id - m_connectors_group_id].pos = pos; + update_raycasters_for_picking_transform(); + } +} + +void GLGizmoCut3D::on_dragging(const UpdateData& data) +{ + if (m_hover_id < 0) + return; + if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove || m_hover_id == CutPlaneYMove) + dragging_grabber_move(data); + else if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) + dragging_grabber_rotation(data); + else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) + dragging_connector(data); + check_and_update_connectors_state(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); +} + +void GLGizmoCut3D::on_start_dragging() +{ + m_angle = 0.0; + if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move connector"), UndoRedo::SnapshotType::GizmoAction); + + if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) + m_start_dragging_m = m_rotation_m; +} + +void GLGizmoCut3D::on_stop_dragging() +{ + if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) { + m_angle_arc.reset(); + m_angle = 0.0; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_start_dragging_m = m_rotation_m; + } + else if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove|| m_hover_id == CutPlaneYMove) { + if (m_was_cut_plane_dragged) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_ar_plane_center = m_plane_center; + } + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); + //check_and_update_connectors_state(); +} + +void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=false*/) +{ + BoundingBoxf3 tbb = m_transformed_bounding_box; + if (update_tbb) { + Vec3d normal = m_rotation_m.inverse() * Vec3d(m_plane_center - center_pos); + tbb.translate(normal.z() * Vec3d::UnitZ()); + } + + bool can_set_center_pos = false; + { + double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove.depth) : */0.5; + if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val) + can_set_center_pos = true; + else { + const double old_dist = (m_bb_center - m_plane_center).norm(); + const double new_dist = (m_bb_center - center_pos).norm(); + // check if forcing is reasonable + if (new_dist < old_dist) + can_set_center_pos = true; + } + } + + if (can_set_center_pos) { + m_transformed_bounding_box = tbb; + m_plane_center = center_pos; + m_center_offset = m_plane_center - m_bb_center; + } +} + +BoundingBoxf3 GLGizmoCut3D::bounding_box() const { BoundingBoxf3 ret; const Selection& selection = m_parent.get_selection(); const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int i : idxs) { const GLVolume* volume = selection.get_volume(i); - if (!volume->is_modifier) + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) ret.merge(volume->transformed_convex_hull_bounding_box()); } return ret; } -void GLGizmoCut::update_contours() +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, const Transform3d& rotation_m/* = Transform3d::Identity()*/) const { const Selection& selection = m_parent.get_selection(); - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); - const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; - const int instance_idx = selection.get_instance_idx(); + const auto first_volume = selection.get_first_volume(); + Vec3d instance_offset = first_volume->get_instance_offset(); + instance_offset[Z] += first_volume->get_sla_shift_z(); - if (0.0 < m_cut_z && m_cut_z < m_max_z) { - if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) { - m_cut_contours.cut_z = m_cut_z; + const auto cut_matrix = Transform3d::Identity() * rotation_m.inverse() * translation_transform(instance_offset - plane_center); - if (m_cut_contours.object_id != model_object->id()) - m_cut_contours.mesh = model_object->raw_mesh(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + BoundingBoxf3 ret; + for (unsigned int i : idxs) { + const GLVolume* volume = selection.get_volume(i); + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) { - m_cut_contours.position = box.center(); - m_cut_contours.shift = Vec3d::Zero(); - m_cut_contours.object_id = model_object->id(); - m_cut_contours.instance_idx = instance_idx; - m_cut_contours.contours.reset(); - - MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix(); - const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params); - if (!polys.empty()) { - m_cut_contours.contours.init_from(polys, static_cast(m_cut_z)); - m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); - } - } - else if (box.center() != m_cut_contours.position) { - m_cut_contours.shift = box.center() - m_cut_contours.position; + const auto instance_matrix = volume->get_instance_transformation().get_matrix(true); + auto volume_trafo = instance_matrix * volume->get_volume_transformation().get_matrix(); + ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * volume_trafo)); } } - else - m_cut_contours.contours.reset(); + return ret; } +void GLGizmoCut3D::update_bb() +{ + const BoundingBoxf3 box = bounding_box(); + if (!box.defined) + return; + if (!m_max_pos.isApprox(box.max) || !m_min_pos.isApprox(box.min)) { + + m_bounding_box = box; + + // check, if mode is set to Planar, when object has a connectors + if (const int object_idx = m_parent.get_selection().get_object_idx(); + object_idx >= 0 && !wxGetApp().plater()->model().objects[object_idx]->cut_connectors.empty()) + m_mode = size_t(CutMode::cutPlanar); + + invalidate_cut_plane(); + reset_cut_by_contours(); + apply_color_clip_plane_colors(); + + m_max_pos = box.max; + m_min_pos = box.min; + m_bb_center = box.center(); + m_transformed_bounding_box = transformed_bounding_box(m_bb_center); + if (box.contains(m_center_offset)) + set_center_pos(m_bb_center + m_center_offset); + else + set_center_pos(m_bb_center); + + m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; + + m_radius = box.radius(); + m_grabber_connection_len = 0.5 * m_radius;// std::min(0.75 * m_radius, 35.0); + m_grabber_radius = m_grabber_connection_len * 0.85; + + m_snap_coarse_in_radius = m_grabber_radius / 3.0; + m_snap_coarse_out_radius = m_snap_coarse_in_radius * 2.; + m_snap_fine_in_radius = m_grabber_connection_len * 0.85; + m_snap_fine_out_radius = m_grabber_connection_len * 1.15; + + // input params for cut with tongue and groove + m_groove.depth = m_groove.depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); + m_groove.width = m_groove.width_init = 4.0f * m_groove.depth; + m_groove.flaps_angle = m_groove.flaps_angle_init = float(PI) / 3.f; + m_groove.angle = m_groove.angle_init = 0.f; + m_plane.reset(); + m_cone.reset(); + m_sphere.reset(); + m_cube.reset(); + m_grabber_connection.reset(); + m_circle.reset(); + m_scale.reset(); + m_snap_radii.reset(); + m_reference_radius.reset(); + + on_unregister_raycasters_for_picking(); + + clear_selection(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info(); + selection && selection->model_object()) + m_selected.resize(selection->model_object()->cut_connectors.size(), false); + } +} + +void GLGizmoCut3D::init_picking_models() +{ + if (!m_cone.model.is_initialized()) { + indexed_triangle_set its = its_make_cone(1.0, 1.0, PI / 12.0); + m_cone.model.init_from(its); + m_cone.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + if (!m_sphere.model.is_initialized()) { + indexed_triangle_set its = its_make_sphere(1.0, PI / 12.0); + m_sphere.model.init_from(its); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + if (!m_cube.model.is_initialized()) { + indexed_triangle_set its = its_make_cube(1., 1., 1.); + m_cube.model.init_from(its); + m_cube.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + + if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { + const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); + indexed_triangle_set its = m_mode == size_t(CutMode::cutTongueAndGroove) ? its_make_groove_plane() : + its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + + m_plane.model.init_from(its); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + + if (m_shapes.empty()) + init_connector_shapes(); +} + +void GLGizmoCut3D::init_rendering_items() +{ + if (!m_grabber_connection.is_initialized()) + m_grabber_connection.init_from(its_make_line(Vec3f::Zero(), Vec3f::UnitZ())); + if (!m_circle.is_initialized()) + init_from_circle(m_circle, m_grabber_radius); + if (!m_scale.is_initialized()) + init_from_scale(m_scale, m_grabber_radius); + if (!m_snap_radii.is_initialized()) + init_from_snap_radii(m_snap_radii, m_grabber_radius); + if (!m_reference_radius.is_initialized()) { + m_reference_radius.init_from(its_make_line(Vec3f::Zero(), m_grabber_connection_len * Vec3f::UnitX())); + m_reference_radius.set_color(ColorRGBA::WHITE()); + } + if (!m_angle_arc.is_initialized() || m_angle != 0.0) + init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); +} + +void GLGizmoCut3D::render_clipper_cut() +{ + if (! m_connectors_editing) + ::glDisable(GL_DEPTH_TEST); + + GLboolean cull_face = GL_FALSE; + ::glGetBooleanv(GL_CULL_FACE, &cull_face); + ::glDisable(GL_CULL_FACE); + m_c->object_clipper()->render_cut(m_part_selection.get_ignored_contours_ptr()); + if (cull_face) + ::glEnable(GL_CULL_FACE); + + if (! m_connectors_editing) + ::glEnable(GL_DEPTH_TEST); +} + +void GLGizmoCut3D::PartSelection::add_object(const ModelObject* object) +{ + m_model = Model(); + m_model.add_object(*object); + + const double sla_shift_z = wxGetApp().plater()->canvas3D()->get_selection().get_first_volume()->get_sla_shift_z(); + if (!is_approx(sla_shift_z, 0.)) { + Vec3d inst_offset = model_object()->instances[m_instance_idx]->get_offset(); + inst_offset[Z] += sla_shift_z; + model_object()->instances[m_instance_idx]->set_offset(inst_offset); + } +} + + +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) + : m_instance_idx(instance_idx_in) +{ + Cut cut(mo, instance_idx_in, cut_matrix); + add_object(cut.perform_with_plane().front()); + + const ModelVolumePtrs& volumes = model_object()->volumes; + + // split to parts + for (int id = int(volumes.size())-1; id >= 0; id--) + if (volumes[id]->is_splittable()) + volumes[id]->split(1); + + m_parts.clear(); + for (const ModelVolume* volume : volumes) { + assert(volume != nullptr); + m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true, !volume->is_model_part()}); + m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); + m_parts.back().glmodel.init_from(volume->mesh()); + + // Now check whether this part is below or above the plane. + Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); + Vec3f pos = (tr * center).cast(); + Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); + for (const Vec3f& v : volume->mesh().its.vertices) { + double p = (v - pos).dot(norm); + if (std::abs(p) > EPSILON) { + m_parts.back().selected = p > 0.; + break; + } + } + } + + // Now go through the contours and create a map from contours to parts. + m_contour_points.clear(); + m_contour_to_parts.clear(); + m_debug_pts = std::vector>(m_parts.size(), std::vector()); + if (std::vector pts = oc.point_per_contour();! pts.empty()) { + + m_contour_to_parts.resize(pts.size()); + + for (size_t pt_idx=0; pt_idxinstances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[part_id]->get_offset())).inverse(); + for (double d : {-1., 1.}) { + const Vec3d dir_mesh = d * tr.linear().inverse().transpose() * normal; + const Vec3d src = tr * (m_contour_points[pt_idx] + d*0.01 * normal); + AABBMesh::hit_result hit = aabb.query_ray_hit(src, dir_mesh); + + m_debug_pts[part_id].emplace_back(src); + + if (hit.is_inside()) { + // This part belongs to this point. + if (d == 1.) + m_contour_to_parts[pt_idx].first.emplace_back(part_id); + else + m_contour_to_parts[pt_idx].second.emplace_back(part_id); + } + } + } + } + + } + + + m_valid = true; +} + +// In CutMode::cutTongueAndGroove we use PartSelection just for rendering +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in) + : m_instance_idx (instance_idx_in) +{ + add_object(object); + + m_parts.clear(); + + for (const ModelVolume* volume : object->volumes) { + assert(volume != nullptr); + m_parts.emplace_back(Part{ GLModel(), MeshRaycaster(volume->mesh()), true, !volume->is_model_part() }); + m_parts.back().glmodel.init_from(volume->mesh()); + + // Now check whether this part is below or above the plane. + m_parts.back().selected = volume->is_from_upper(); + } + + m_valid = true; +} + +void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_model) +{ + if (! valid()) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + + if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("emission_factor", 0.f); + + // FIXME: Cache the transforms. + + const Vec3d inst_offset = model_object()->instances[m_instance_idx]->get_offset(); + const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); + + const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05; + + for (size_t id=0; idset_uniform("view_model_matrix", view_inst_matrix * model_object()->volumes[id]->get_matrix()); + if (m_parts[id].is_modifier) { + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + } + m_parts[id].glmodel.set_color(m_parts[id].is_modifier ? MODIFIER_COLOR : (m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR)); + m_parts[id].glmodel.render(); + if (m_parts[id].is_modifier) + glsafe(::glDisable(GL_BLEND)); + } + + shader->stop_using(); + } + + + + // { // Debugging render: + + // static int idx = -1; + // ImGui::Begin("DEBUG"); + // for (int i=0; i= m_parts.size()) + // idx = -1; + // ImGui::End(); + + // ::glDisable(GL_DEPTH_TEST); + // if (valid()) { + // for (size_t i=0; i GLGizmoCut3D::PartSelection::get_cut_parts() +{ + std::vector parts; + + for (const auto& part : m_parts) + parts.push_back({part.selected, part.is_modifier}); + + return parts; +} + + +void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) +{ + // FIXME: Cache the transforms. + const Camera& camera = wxGetApp().plater()->get_camera(); + const Vec3d& camera_pos = camera.get_position(); + + Vec3f pos; + Vec3f normal; + + std::vector> hits_id_and_sqdist; + + for (size_t id=0; idvolumes[id]->get_offset(); + Transform3d tr = translation_transform(model_object()->instances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[id]->get_offset()); + if (m_parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { + hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); + } + } + if (! hits_id_and_sqdist.empty()) { + size_t id = std::min_element(hits_id_and_sqdist.begin(), hits_id_and_sqdist.end(), + [](const std::pair& a, const std::pair& b) { return a.second < b.second; })->first; + m_parts[id].selected = ! m_parts[id].selected; + + // And now recalculate the contours which should be ignored. + m_ignored_contours.clear(); + size_t cont_id = 0; + for (const auto& [parts_above, parts_below] : m_contour_to_parts) { + for (size_t upper : parts_above) { + bool upper_sel = m_parts[upper].selected; + if (std::find_if(parts_below.begin(), parts_below.end(), [this, &upper_sel](const size_t& i) { return m_parts[i].selected == upper_sel; }) != parts_below.end()) { + m_ignored_contours.emplace_back(cont_id); + break; + } + } + ++cont_id; + } + } +} + +void GLGizmoCut3D::PartSelection::turn_over_selection() +{ + for (Part& part : m_parts) + part.selected = !part.selected; +} + +void GLGizmoCut3D::on_render() +{ + if (m_state == On) { + // This gizmo is showing the object elevated. Tell the common + // SelectionInfo object to lie about the actual shift. + //m_c->selection_info()->set_use_shift(true); + } + + // check objects visibility + toggle_model_objects_visibility(); + + update_clipper(); + + init_picking_models(); + + init_rendering_items(); + + render_connectors(); + + if (!m_connectors_editing) + m_part_selection.render(nullptr, m_sphere.model); + else + m_part_selection.render(&m_cut_normal, m_sphere.model); + + render_clipper_cut(); + + if (!m_hide_cut_plane && !m_connectors_editing) { + render_cut_plane(); + render_cut_plane_grabbers(); + } + + render_cut_line(); + + m_selection_rectangle.render(m_parent); +} + +void GLGizmoCut3D::render_debug_input_window(float x) +{ + return; + m_imgui->begin(wxString("DEBUG")); + + m_imgui->end(); +/* + static bool hide_clipped = false; + static bool fill_cut = false; + static float contour_width = 0.4f; + + m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); + if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); + m_imgui->checkbox("fill_cut", fill_cut); + m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); + if (auto oc = m_c->object_clipper()) + oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); +*/ + ImGui::PushItemWidth(0.5f * m_label_width); + if (auto oc = m_c->object_clipper(); oc && m_imgui->slider_float("contour_width", &m_contour_width, 0.f, 3.f)) + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + + ImGui::Separator(); + + if (m_imgui->checkbox(("Render cut plane as disc"), m_cut_plane_as_circle)) + m_plane.reset(); + + ImGui::PushItemWidth(0.5f * m_label_width); + if (m_imgui->slider_float("cut_plane_radius_koef", &m_cut_plane_radius_koef, 1.f, 2.f)) + m_plane.reset(); + + m_imgui->end(); +} + +void GLGizmoCut3D::unselect_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + validate_connector_settings(); +} + +void GLGizmoCut3D::select_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), true); + m_selected_count = int(m_selected.size()); +} + +void GLGizmoCut3D::apply_selected_connectors(std::function apply_fn) +{ + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) + apply_fn(idx); + check_and_update_connectors_state(); + update_raycasters_for_picking_transform(); +} + +void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, float x, float y, float bottom_limit) +{ + // Connectors section + + ImGui::Separator(); + + // WIP : Auto : Need to implement + // m_imgui->text(_L("Mode")); + // render_connect_mode_radio_button(CutConnectorMode::Auto); + // render_connect_mode_radio_button(CutConnectorMode::Manual); + + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Connectors"]); + + m_imgui->disabled_begin(connectors.empty()); + ImGui::SameLine(m_label_width); + const std::string act_name = _u8L("Remove connectors"); + if (render_reset_button("connectors", act_name)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction); + reset_connectors(); + } + m_imgui->disabled_end(); + + render_flip_plane_button(m_connectors_editing && connectors.empty()); + + m_imgui->text(m_labels_map["Type"]); + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.00f, 0.00f, 0.00f, 1.00f)); + bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); + type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + type_changed |= render_connect_type_radio_button(CutConnectorType::Snap); + if (type_changed) + apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); + ImGui::PopStyleColor(1); + + m_imgui->disabled_begin(m_connector_type != CutConnectorType::Plug); + if (type_changed && m_connector_type == CutConnectorType::Dowel) { + m_connector_style = int(CutConnectorStyle::Prism); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + } + if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(m_connector_type == CutConnectorType::Snap); + if (type_changed && m_connector_type == CutConnectorType::Snap) { + m_connector_shape_id = int(CutConnectorShape::Circle); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + } + if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + m_imgui->disabled_end(); + + const float depth_min_value = m_connector_type == CutConnectorType::Snap ? m_connector_size : -0.1f; + if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance, depth_min_value)) + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_depth_ratio > 0) + connectors[idx].height = m_connector_depth_ratio; + if (m_connector_depth_ratio_tolerance >= 0) + connectors[idx].height_tolerance = m_connector_depth_ratio_tolerance; + }); + + if (render_slider_double_input(m_labels_map["Size"], m_connector_size, m_connector_size_tolerance)) + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_size > 0) + connectors[idx].radius = 0.5f * m_connector_size; + if (m_connector_size_tolerance >= 0) + connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance; + }); + + if (render_angle_input(m_labels_map["Rotation"], m_connector_angle, 0.f, 0.f, 180.f)) + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].z_angle = m_connector_angle; + }); + + if (m_connector_type == CutConnectorType::Snap) { + render_snap_specific_input(_u8L("Bulge"), _L("Bulge proportion related to radius"), m_snap_bulge_proportion, 0.15f, 5.f, 100.f * m_snap_space_proportion); + render_snap_specific_input(_u8L("Space"), _L("Space proportion related to radius"), m_snap_space_proportion, 0.3f, 10.f, 50.f); + } + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + show_tooltip_information(x, get_cur_y); + + float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::SameLine(); + if (m_imgui->button(_L("Confirm connectors"))) { + unselect_all_connectors(); + set_connectors_editing(false); + } + + ImGui::SameLine(m_label_width + m_editing_window_width - m_imgui->calc_text_size(_L("Cancel")).x - m_imgui->get_style_scaling() * 8); + + if (m_imgui->button(_L("Cancel"))) { + reset_connectors(); + set_connectors_editing(false); + } + + ImGui::PopStyleVar(2); +} + +void GLGizmoCut3D::render_build_size() +{ + double koef = m_imperial_units ? GizmoObjectManipulation::mm_to_in : 1.0; + wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); + + Vec3d tbb_sz = m_transformed_bounding_box.size(); + wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build Volume")); + ImGui::SameLine(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); +} + +void GLGizmoCut3D::reset_cut_plane() +{ + m_angle_arc.reset(); + m_transformed_bounding_box = transformed_bounding_box(m_bb_center); + set_center(m_bb_center); + m_start_dragging_m = m_rotation_m = Transform3d::Identity(); + m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); + m_parent.request_extra_frame(); +} + +void GLGizmoCut3D::invalidate_cut_plane() +{ + m_rotation_m = Transform3d::Identity(); + m_plane_center = Vec3d::Zero(); + m_min_pos = Vec3d::Zero(); + m_max_pos = Vec3d::Zero(); + m_bb_center = Vec3d::Zero(); + m_center_offset = Vec3d::Zero(); +} + +void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) +{ + if (m_connectors_editing == connectors_editing) + return; + + m_connectors_editing = connectors_editing; + update_raycasters_for_picking(); + + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + + m_parent.request_extra_frame(); +} + +void GLGizmoCut3D::flip_cut_plane() +{ + m_rotation_m = m_rotation_m * rotation_transform(PI * Vec3d::UnitX()); + m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m); + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Flip cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_start_dragging_m = m_rotation_m; + + update_clipper(); + m_part_selection.turn_over_selection(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); +} + +void GLGizmoCut3D::reset_cut_by_contours() +{ + m_part_selection = PartSelection(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + if (m_dragging || m_groove_editing || !has_valid_groove()) + return; + process_contours(); + } + else + toggle_model_objects_visibility(); +} + +void GLGizmoCut3D::process_contours() +{ + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + + const int instance_idx = selection.get_instance_idx(); + if (instance_idx < 0) + return; + const int object_idx = selection.get_object_idx(); + + wxBusyCursor wait; + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + if (has_valid_groove()) { + Cut cut(model_objects[object_idx], instance_idx, get_cut_matrix(selection)); + const ModelObjectPtrs& new_objects = cut.perform_with_groove(m_groove, m_rotation_m, true); + if (!new_objects.empty()) + m_part_selection = PartSelection(new_objects.front(), instance_idx); + } + } + else { + reset_cut_by_contours(); + m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); + } + + toggle_model_objects_visibility(); +} + +void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) +{ + ImGui::SameLine(); + + if (m_hover_id == CutPlane) + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered)); + + m_imgui->disabled_begin(disable_pred); + if (m_imgui->button(_L("Flip cut plane"))) + flip_cut_plane(); + m_imgui->disabled_end(); + + if (m_hover_id == CutPlane) + ImGui::PopStyleColor(); +} + +void GLGizmoCut3D::add_vertical_scaled_interval(float interval) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.y += m_imgui->scaled(interval); +} + +void GLGizmoCut3D::add_horizontal_scaled_interval(float interval) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.x += m_imgui->scaled(interval); +} + +void GLGizmoCut3D::add_horizontal_shift(float shift) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.x += shift; +} + +void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) +{ + ImGui::SameLine(); + const float radius = 0.5f * size; + ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + pos.x += size; + pos.y += 1.25f * radius; + ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6); + m_imgui->text(" "); +} + +void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance) +{ + bool is_changed{false}; + + float val = in_val; + float tolerance = in_tolerance; + if (render_slider_double_input(label, val, tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) { + if (m_imgui->get_last_slider_status().can_take_snapshot) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), GUI::format("%1%: %2%", _u8L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + m_imgui->get_last_slider_status().invalidate_snapshot(); + m_groove_editing = true; + } + in_val = val; + in_tolerance = tolerance; + is_changed = true; + } + + ImGui::SameLine(); + + m_imgui->disabled_begin(is_approx(in_val, init_val) && is_approx(in_tolerance, 0.1f)); + const std::string act_name = _u8L("Reset"); + if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), GUI::format("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); + in_val = init_val; + in_tolerance = 0.1f; + is_changed = true; + } + m_imgui->disabled_end(); + + if (is_changed) { + update_plane_model(); + reset_cut_by_contours(); + } + + if (m_is_slider_editing_done) { + m_groove_editing = false; + reset_cut_by_contours(); + } +} + +bool GLGizmoCut3D::render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +{ + // -------- [ ] + // slider_with + item_in_gap + input_width + double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35; + double item_in_gap = 0.01 * m_editing_window_width; + double input_width = 0.29 * m_editing_window_width; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(slider_with); + + double left_width = m_label_width + slider_with + item_in_gap; + + bool is_changed{ false }; + + float val = rad2deg(in_val); + const float old_val = val; + + const std::string format = "%.0f " + _u8L("°"); + m_imgui->bbl_slider_float_style(("##angle_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); + + ImGui::SameLine(left_width); + ImGui::PushItemWidth(input_width); + ImGui::BBLDragFloat(("##angle_input_" + label).c_str(), &val, 0.05f, min_val, max_val, format.c_str()); + + m_is_slider_editing_done |= m_imgui->get_last_slider_status().deactivated_after_edit; + if (!is_approx(old_val, val)) { + if (m_imgui->get_last_slider_status().can_take_snapshot) { + // TRN: This is an entry in the Undo/Redo stack. The whole line will be 'Edited: (name of whatever was edited)'. + Plater::TakeSnapshot snapshot(wxGetApp().plater(), GUI::format("%1%: %2%", _L("Edited"), label), UndoRedo::SnapshotType::GizmoAction); + m_imgui->get_last_slider_status().invalidate_snapshot(); + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + m_groove_editing = true; + } + in_val = deg2rad(val); + is_changed = true; + } + + ImGui::SameLine(); + + m_imgui->disabled_begin(is_approx(in_val, init_val)); + const std::string act_name = _u8L("Reset"); + if (render_reset_button(("##angle_" + label + act_name).c_str(), act_name)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), GUI::format("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); + in_val = init_val; + is_changed = true; + } + m_imgui->disabled_end(); + + return is_changed; +} + +void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +{ + if (render_angle_input(label, in_val, init_val, min_val, max_val)) { + update_plane_model(); + reset_cut_by_contours(); + } + + if (m_is_slider_editing_done) { + m_groove_editing = false; + reset_cut_by_contours(); + } +} + +void GLGizmoCut3D::render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val) +{ + // -------- [ ] + // slider_with + item_in_gap + input_width + double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35; + double item_in_gap = 0.01 * m_editing_window_width; + double input_width = 0.29 * m_editing_window_width; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(slider_with); + + double left_width = m_label_width + slider_with + item_in_gap; + + bool is_changed = false; + const std::string format = "%.0f %%"; + + float val = in_val * 100.f; + const float old_val = val; + m_imgui->bbl_slider_float_style(("##snap_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, tooltip); + + ImGui::SameLine(left_width); + ImGui::PushItemWidth(input_width); + ImGui::BBLDragFloat(("##snap_input_" + label).c_str(), &val, 0.05f, min_val, max_val, format.c_str()); + + if (!is_approx(old_val, val)) { + in_val = val * 0.01f; + is_changed = true; + } + + ImGui::SameLine(); + + m_imgui->disabled_begin(is_approx(in_val, init_val)); + const std::string act_name = _u8L("Reset"); + if (render_reset_button(("##snap_" + label + act_name).c_str(), act_name)) { + in_val = init_val; + is_changed = true; + } + m_imgui->disabled_end(); + + if (is_changed) { + update_connector_shape(); + update_raycasters_for_picking(); + } +} + +void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, float x, float y, float bottom_limit) +{ +// if (m_mode == size_t(CutMode::cutPlanar)) { + CutMode mode = CutMode(m_mode); + if (mode == CutMode::cutPlanar || mode == CutMode::cutTongueAndGroove) { + const bool has_connectors = !connectors.empty(); + + m_imgui->disabled_begin(has_connectors); + if (render_cut_mode_combo()) + mode = CutMode(m_mode); + m_imgui->disabled_end(); + + render_build_size(); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Cut position") + ": "); + ImGui::SameLine(); + render_move_center_input(Z); + ImGui::SameLine(); + + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center); + m_imgui->disabled_begin(is_cut_plane_init); + std::string act_name = _u8L("Reset cutting plane"); + if (render_reset_button("cut_plane", into_u8(act_name))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction); + reset_cut_plane(); + } + m_imgui->disabled_end(); + +// render_flip_plane_button(); + + if (mode == CutMode::cutPlanar) { + add_vertical_scaled_interval(0.75f); + + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); + if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) + set_connectors_editing(true); + m_imgui->disabled_end(); + + ImGui::SameLine(1.5f * m_control_width); + + m_imgui->disabled_begin(is_cut_plane_init && !has_connectors); + act_name = _u8L("Reset cut"); + if (m_imgui->button(act_name, _u8L("Reset cutting plane and remove connectors"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction); + reset_cut_plane(); + reset_connectors(); + } + m_imgui->disabled_end(); + } + else if (mode == CutMode::cutTongueAndGroove) { + m_is_slider_editing_done = false; + ImGui::Separator(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": "); + render_groove_float_input(m_labels_map["Depth"], m_groove.depth, m_groove.depth_init, m_groove.depth_tolerance); + render_groove_float_input(m_labels_map["Width"], m_groove.width, m_groove.width_init, m_groove.width_tolerance); + render_groove_angle_input(m_labels_map["Flap Angle"], m_groove.flaps_angle, m_groove.flaps_angle_init, 30.f, 120.f); + render_groove_angle_input(m_labels_map["Groove Angle"], m_groove.angle, m_groove.angle_init, 0.f, 15.f); + } + + ImGui::Separator(); + + // render "After Cut" section + + float label_width = 0; + for (const wxString &label : {_L("Upper part"), _L("Lower part")}) { + const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f); + if (label_width < width) + label_width = width; + } + + auto render_part_action_line = [this, label_width, &connectors](const wxString &label, const wxString &suffix, bool &keep_part, + bool &place_on_cut_part, bool &rotate_part) { + bool keep = true; + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + + ImGui::SameLine(label_width); + + m_imgui->disabled_begin(!connectors.empty() || m_keep_as_parts); + m_imgui->bbl_checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); + m_imgui->disabled_end(); + + ImGui::SameLine(); + + m_imgui->disabled_begin(!keep_part || m_keep_as_parts); + if (m_imgui->bbl_checkbox(_L("Place on cut") + suffix, place_on_cut_part)) + rotate_part = false; + ImGui::SameLine(); + if (m_imgui->bbl_checkbox(_L("Flip") + suffix, rotate_part)) + place_on_cut_part = false; + m_imgui->disabled_end(); + }; + + m_imgui->text(_L("After cut") + ": "); + render_part_action_line(_L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); + render_part_action_line(_L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); + + m_imgui->disabled_begin(has_connectors); + m_imgui->bbl_checkbox(_L("Cut to parts"), m_keep_as_parts); + if (m_keep_as_parts) { + m_keep_upper = true; + m_keep_lower = true; + } + m_imgui->disabled_end(); + } + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + show_tooltip_information(x, get_cur_y); + + float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::SameLine(); + m_imgui->disabled_begin(!can_perform_cut()); + if(m_imgui->button(_L("Perform cut"))) + perform_cut(m_parent.get_selection()); + m_imgui->disabled_end(); + + ImGui::PopStyleVar(2); +} + +void GLGizmoCut3D::validate_connector_settings() +{ + if (m_connector_depth_ratio < 0.f) + m_connector_depth_ratio = 3.f; + if (m_connector_depth_ratio_tolerance < 0.f) + m_connector_depth_ratio_tolerance = 0.1f; + if (m_connector_size < 0.f) + m_connector_size = 2.5f; + if (m_connector_size_tolerance < 0.f) + m_connector_size_tolerance = 0.f; + if (m_connector_angle < 0.f || m_connector_angle > float(PI) ) + m_connector_angle = 0.f; + + if (m_connector_type == CutConnectorType::Undef) + m_connector_type = CutConnectorType::Plug; + if (m_connector_style == int(CutConnectorStyle::Undef)) + m_connector_style = int(CutConnectorStyle::Prism); + if (m_connector_shape_id == int(CutConnectorShape::Undef)) + m_connector_shape_id = int(CutConnectorShape::Circle); +} + +void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) +{ + m_imperial_units = wxGetApp().app_config->get_bool("use_inches"); + m_control_width = m_imgui->get_font_size() * 9.f; + + m_editing_window_width = 1.45 * m_control_width + 11; + + if (m_connectors_editing && m_selected_count > 0) { + float depth_ratio { UndefFloat }; + float depth_ratio_tolerance { UndefFloat }; + float radius { UndefFloat }; + float radius_tolerance { UndefFloat }; + float angle { UndefFloat }; + CutConnectorType type { CutConnectorType::Undef }; + CutConnectorStyle style { CutConnectorStyle::Undef }; + CutConnectorShape shape { CutConnectorShape::Undef }; + + bool is_init = false; + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + const CutConnector& connector = connectors[idx]; + if (!is_init) { + depth_ratio = connector.height; + depth_ratio_tolerance = connector.height_tolerance; + radius = connector.radius; + radius_tolerance = connector.radius_tolerance; + angle = connector.z_angle; + type = connector.attribs.type; + style = connector.attribs.style; + shape = connector.attribs.shape; + + if (m_selected_count == 1) + break; + is_init = true; + } + else { + if (!is_approx(depth_ratio, connector.height)) + depth_ratio = UndefFloat; + if (!is_approx(depth_ratio_tolerance, connector.height_tolerance)) + depth_ratio_tolerance = UndefFloat; + if (!is_approx(radius,connector.radius)) + radius = UndefFloat; + if (!is_approx(radius_tolerance, connector.radius_tolerance)) + radius_tolerance = UndefFloat; + if (!is_approx(angle, connector.z_angle)) + angle = UndefFloat; + + if (type != connector.attribs.type) + type = CutConnectorType::Undef; + if (style != connector.attribs.style) + style = CutConnectorStyle::Undef; + if (shape != connector.attribs.shape) + shape = CutConnectorShape::Undef; + } + } + + m_connector_depth_ratio = depth_ratio; + m_connector_depth_ratio_tolerance = depth_ratio_tolerance; + m_connector_size = 2.f * radius; + m_connector_size_tolerance = 2.f * radius_tolerance; + m_connector_type = type; + m_connector_angle = angle; + m_connector_style = int(style); + m_connector_shape_id = int(shape); + } + + if (m_label_width == 0.f) { + for (const auto& item : m_labels_map) { + const float width = m_imgui->calc_text_size(item.second).x; + if (m_label_width < width) + m_label_width = width; + } + m_label_width += m_imgui->scaled(1.f); + m_label_width += ImGui::GetStyle().WindowPadding.x; + } +} + +void GLGizmoCut3D::render_input_window_warning() const +{ + if (! m_invalid_connectors_idxs.empty()) { + wxString out = /*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Invalid connectors detected") + ":"; + if (m_info_stats.outside_cut_contour > size_t(0)) + out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of cut contour", "%1$d connectors are out of cut contour", m_info_stats.outside_cut_contour), + m_info_stats.outside_cut_contour); + if (m_info_stats.outside_bb > size_t(0)) + out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of object", "%1$d connectors are out of object", m_info_stats.outside_bb), + m_info_stats.outside_bb); + if (m_info_stats.is_overlap) + out += "\n - " + _L("Some connectors are overlapped"); + m_imgui->text(out); + } + if (!m_keep_upper && !m_keep_lower) + m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Select at least one object to keep after cutting.")); + if (!has_valid_contour()) + m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane is placed out of object")); + else if (!has_valid_groove()) + m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane with groove is invalid")); +} + +void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) +{ + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + + init_input_window_data(connectors); + + if (m_connectors_editing) // connectors mode + render_connectors_input_window(connectors, x, y, bottom_limit); + else + render_cut_plane_input_window(connectors, x, y, bottom_limit); + + render_input_window_warning(); + + GizmoImguiEnd(); + + // Orca + ImGuiWrapper::pop_toolbar_style(); + + if (!m_connectors_editing) // connectors mode + render_debug_input_window(x); +} + +void GLGizmoCut3D::show_tooltip_information(float x, float y) +{ + auto &shortcuts = m_connectors_editing ? m_shortcuts_connector : m_shortcuts_cut; + + float caption_max = 0.f; + for (const auto &short_cut : shortcuts) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(short_cut.first).x); + } + + ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); + ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + + caption_max += m_imgui->calc_text_size(": ").x + 35.f; + + float font_size = ImGui::GetFontSize(); + ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, ImGui::GetStyle().FramePadding.y}); + ImGui::ImageButton3(normal_id, hover_id, button_size); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip2(ImVec2(x, y)); + auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { + m_imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption); + ImGui::SameLine(caption_max); + m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); + }; + + for (const auto &short_cut : shortcuts) + draw_text_with_caption(short_cut.first + ": ", short_cut.second); + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); +} + +bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) +{ + // check if connector pos is out of clipping plane + if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(cur_pos) == -1) { + m_info_stats.outside_cut_contour++; + return true; + } + + // check if connector bottom contour is out of clipping plane + const CutConnector& cur_connector = connectors[idx]; + const CutConnectorShape shape = CutConnectorShape(cur_connector.attribs.shape); + const int sectorCount = shape == CutConnectorShape::Triangle ? 3 : + shape == CutConnectorShape::Square ? 4 : + shape == CutConnectorShape::Circle ? 60: // supposably, 60 points are enough for conflict detection + shape == CutConnectorShape::Hexagon ? 6 : 1 ; + + indexed_triangle_set mesh; + auto& vertices = mesh.vertices; + vertices.reserve(sectorCount + 1); + + float fa = 2 * PI / sectorCount; + auto vec = Eigen::Vector2f(0, cur_connector.radius); + for (float angle = 0; angle < 2.f * PI; angle += fa) { + Vec2f p = Eigen::Rotation2Df(angle) * vec; + vertices.emplace_back(Vec3f(p(0), p(1), 0.f)); + } + its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); + + for (const Vec3f& vertex : vertices) { + if (m_c->object_clipper()) { + int contour_idx = m_c->object_clipper()->is_projection_inside_cut(vertex.cast()); + bool is_invalid = (contour_idx == -1); + if (m_part_selection.valid() && ! is_invalid) { + assert(contour_idx >= 0); + const std::vector& ignored = *(m_part_selection.get_ignored_contours_ptr()); + is_invalid = (std::find(ignored.begin(), ignored.end(), size_t(contour_idx)) != ignored.end()); + } + if (is_invalid) { + m_info_stats.outside_cut_contour++; + return true; + } + } + } + + return false; +} + +bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) +{ + if (is_outside_of_cut_contour(idx, connectors, cur_pos)) + return true; + + const CutConnector& cur_connector = connectors[idx]; + + const Transform3d matrix = translation_transform(cur_pos) * m_rotation_m * + scale_transform(Vec3f(cur_connector.radius, cur_connector.radius, cur_connector.height).cast()); + const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix); + + // check if connector's bounding box is inside the object's bounding box + if (!m_bounding_box.contains(cur_tbb)) { + m_info_stats.outside_bb++; + return true; + } + + // check if connectors are overlapping + for (size_t i = 0; i < connectors.size(); ++i) { + if (i == idx) + continue; + const CutConnector& connector = connectors[i]; + + if ((connector.pos - cur_connector.pos).norm() < double(connector.radius + cur_connector.radius)) { + m_info_stats.is_overlap = true; + return true; + } + } + + return false; +} + +void GLGizmoCut3D::check_and_update_connectors_state() +{ + m_info_stats.invalidate(); + m_invalid_connectors_idxs.clear(); + if (CutMode(m_mode) != CutMode::cutPlanar) + return; + const ModelObject* mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + const CutConnectors& connectors = mo->cut_connectors; + const ModelInstance* mi = mo->instances[inst_id]; + const Vec3d& instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector& connector = connectors[i]; + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); // recalculate connector position to world position + if (is_conflict_for_connector(i, connectors, pos)) + m_invalid_connectors_idxs.emplace_back(i); + } +} + +void GLGizmoCut3D::toggle_model_objects_visibility() +{ + bool has_active_volume = false; + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + for (const std::shared_ptr &raycaster : *raycasters) + if (raycaster->is_active()) { + has_active_volume = true; + break; + } + + if (m_part_selection.valid() && has_active_volume) + m_parent.toggle_model_objects_visibility(false); + else if (!m_part_selection.valid() && !has_active_volume) { + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); + } +} + +void GLGizmoCut3D::render_connectors() +{ + ::glEnable(GL_DEPTH_TEST); + + if (cut_line_processing() || + CutMode(m_mode) != CutMode::cutPlanar || + m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) + return; + + const ModelObject* mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + const CutConnectors& connectors = mo->cut_connectors; + if (connectors.size() != m_selected.size()) { + // #ysFIXME + clear_selection(); + m_selected.resize(connectors.size(), false); + } + + ColorRGBA render_color = CONNECTOR_DEF_COLOR; + + const ModelInstance* mi = mo->instances[inst_id]; + const Vec3d& instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + const bool looking_forward = is_looking_forward(); + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector& connector = connectors[i]; + + float height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); + + // First decide about the color of the point. + assert(std::is_sorted(m_invalid_connectors_idxs.begin(), m_invalid_connectors_idxs.end())); + const bool conflict_connector = std::binary_search(m_invalid_connectors_idxs.begin(), m_invalid_connectors_idxs.end(), i); + if (conflict_connector) + render_color = CONNECTOR_ERR_COLOR; + else // default connector color + render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR; + + if (!m_connectors_editing) + render_color = CONNECTOR_ERR_COLOR; + else if (size_t(m_hover_id - m_connectors_group_id) == i) + render_color = conflict_connector ? HOVERED_ERR_COLOR : + connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR; + else if (m_selected[i]) + render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR; + + const Camera& camera = wxGetApp().plater()->get_camera(); + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prism) { + if (m_connectors_editing) { + height = 0.05f; + if (!looking_forward) + pos += 0.05 * m_clp_normal; + } + else { + if (looking_forward) + pos -= static_cast(height) * m_clp_normal; + else + pos += static_cast(height) * m_clp_normal; + height *= 2; + } + } + else if (!looking_forward) + pos += 0.05 * m_clp_normal; + + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * + scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); + + render_model(m_shapes[connector.attribs].model, render_color, view_model_matrix); + } +} + +bool GLGizmoCut3D::can_perform_cut() const +{ + if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) + return false; + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return has_valid_groove(); + + if (m_part_selection.valid()) + return ! m_part_selection.is_one_object(); + + return true; +} + +bool GLGizmoCut3D::has_valid_groove() const +{ + if (CutMode(m_mode) != CutMode::cutTongueAndGroove) + return true; + + const float flaps_width = -2.f * m_groove.depth / tan(m_groove.flaps_angle); + if (flaps_width > m_groove.width) + return false; + + const Selection& selection = m_parent.get_selection(); + const auto&list = selection.get_volume_idxs(); + // is more volumes selected? + if (list.empty()) + return false; + + const Transform3d cp_matrix = translation_transform(m_plane_center) * m_rotation_m; + + for (size_t id = 0; id < m_groove_vertices.size(); id += 2) { + const Vec3d beg = cp_matrix * m_groove_vertices[id]; + const Vec3d end = cp_matrix * m_groove_vertices[id + 1]; + + bool intersection = false; + for (const unsigned int volume_idx : list) { + const GLVolume* glvol = selection.get_volume(volume_idx); + if (!glvol->is_modifier && + glvol->mesh_raycaster->intersects_line(beg, end - beg, glvol->world_matrix())) { + intersection = true; + break; + } + } + if (!intersection) + return false; + } + + return true; +} + +bool GLGizmoCut3D::has_valid_contour() const +{ + const auto clipper = m_c->object_clipper(); + return clipper && clipper->has_valid_contour(); +} + +void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, int &dowels_count) +{ + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return; + if (m_connector_mode == CutConnectorMode::Manual) { + clear_selection(); + + for (CutConnector&connector : mo->cut_connectors) { + connector.rotation_m = m_rotation_m; + + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prism) + connector.height *= 2; + dowels_count ++; + } + else { + // calculate shift of the connector center regarding to the position on the cut plane + connector.pos += m_cut_normal * 0.5 * double(connector.height); + } + } + apply_cut_connectors(mo, _u8L("Connector")); + } +} + +Transform3d GLGizmoCut3D::get_cut_matrix(const Selection& selection) +{ + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + ModelObject* mo = selection.get_model()->objects[object_idx]; + if (!mo) + return Transform3d::Identity(); + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); + + const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= sla_shift_z; + + return translation_transform(cut_center_offset) * m_rotation_m; +} + +void update_object_cut_id(CutObjectBase& cut_id, ModelObjectCutAttributes attributes, const int dowels_count) +{ + // we don't save cut information, if result will not contains all parts of initial object + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || + !attributes.has(ModelObjectCutAttribute::KeepLower) || + attributes.has(ModelObjectCutAttribute::InvalidateCutInfo)) + return; + + if (cut_id.id().invalid()) + cut_id.init(); + // increase check sum, if it's needed + { + int cut_obj_cnt = -1; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt+= dowels_count; + if (cut_obj_cnt > 0) + cut_id.increase_check_sum(size_t(cut_obj_cnt)); + } +} + +static void check_objects_after_cut(const ModelObjectPtrs& objects) +{ + std::vector err_objects_names; + for (const ModelObject* object : objects) { + std::vector connectors_names; + connectors_names.reserve(object->volumes.size()); + for (const ModelVolume* vol : object->volumes) + if (vol->cut_info.is_connector) + connectors_names.push_back(vol->name); + const size_t connectors_count = connectors_names.size(); + sort_remove_duplicates(connectors_names); + if (connectors_count != connectors_names.size()) + err_objects_names.push_back(object->name); + } + if (err_objects_names.empty()) + return; + + wxString names = from_u8(err_objects_names[0]); + for (size_t i = 1; i < err_objects_names.size(); i++) + names += ", " + from_u8(err_objects_names[i]); + WarningDialog(wxGetApp().plater(), format_wxstr("Objects(%1%) have duplicated connectors. " + "Some connectors may be missing in slicing result.\n" + "Please report to PrusaSlicer team in which scenario this issue happened.\n" + "Thank you.", names)).ShowModal(); +} + +void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id) +{ + for (ModelObject* obj : model.objects) + if (obj->is_cut() && obj->cut_id.has_same_id(cut_id) && !obj->cut_id.is_equal(cut_id)) + obj->cut_id.copy(cut_id); +} + +void GLGizmoCut3D::perform_cut(const Selection& selection) +{ + if (!can_perform_cut()) + return; + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + + wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); + + Plater* plater = wxGetApp().plater(); + ModelObject* mo = plater->model().objects[object_idx]; + if (!mo) + return; + + // deactivate CutGizmo and than perform a cut + m_parent.reset_all_gizmos(); + + // perform cut + { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Cut by Plane")); + + // This shall delete the part selection class and deallocate the memory. + ScopeGuard part_selection_killer([this]() { m_part_selection = PartSelection(); }); + + const bool cut_with_groove = CutMode(m_mode) == CutMode::cutTongueAndGroove; + const bool cut_by_contour = !cut_with_groove && m_part_selection.valid(); + + ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object() : nullptr; + if (cut_mo) + cut_mo->cut_connectors = mo->cut_connectors; + else + cut_mo = mo; + + int dowels_count = 0; + const bool has_connectors = !mo->cut_connectors.empty(); + // update connectors pos as offset of its center before cut performing + apply_connectors_in_model(cut_mo , dowels_count); + + wxBusyCursor wait; + + ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | + only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | + only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | + only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(dowels_count > 0, ModelObjectCutAttribute::CreateDowels) | + only_if(!has_connectors && !cut_with_groove && cut_mo->cut_id.id().invalid(), ModelObjectCutAttribute::InvalidateCutInfo); + + // update cut_id for the cut object in respect to the attributes + update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); + + Cut cut(cut_mo, instance_idx, get_cut_matrix(selection), attributes); + const ModelObjectPtrs& new_objects = cut_by_contour ? cut.perform_by_contour(m_part_selection.get_cut_parts(), dowels_count): + cut_with_groove ? cut.perform_with_groove(m_groove, m_rotation_m) : + cut.perform_with_plane(); + + check_objects_after_cut(new_objects); + + // save cut_id to post update synchronization + const CutObjectBase cut_id = cut_mo->cut_id; + + // update cut results on plater and in the model + plater->apply_cut_object_to_model(object_idx, new_objects); + + synchronize_model_after_cut(plater->model(), cut_id); + } +} + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world, bool respect_contours/* = true*/) +{ + const float sla_shift = m_c->selection_info()->get_sla_shift(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Camera& camera = wxGetApp().plater()->get_camera(); + + // Calculate intersection with the clipping plane. + const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(true); + Vec3d point; + Vec3d direction; + Vec3d hit; + MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, point, direction); + Vec3d normal = -cp->get_normal().cast(); + double den = normal.dot(direction); + if (den != 0.) { + double t = (-cp->get_offset() - normal.dot(point))/den; + hit = (point + t * direction); + } else + return false; + + // Now check if the hit is not obscured by a selected part on this side of the plane. + // FIXME: This would be better solved by remembering which contours are active. We will + // probably need that anyway because there is not other way to find out which contours + // to render. If you want to uncomment it, fix it first. It does not work yet. + /*for (size_t id = 0; id < m_part_selection.parts.size(); ++id) { + if (! m_part_selection.parts[id].selected) { + Vec3f pos, normal; + const ModelObject* model_object = m_part_selection.model_object; + const Vec3d volume_offset = m_part_selection.model_object->volumes[id]->get_offset(); + Transform3d tr = model_object->instances[m_part_selection.instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix(); + if (m_part_selection.parts[id].raycaster.unproject_on_mesh(mouse_position, tr, camera, pos, normal)) + return false; + } + }*/ + + if (respect_contours) + { + // Do not react to clicks outside a contour (or inside a contour that is ignored) + int cont_id = m_c->object_clipper()->is_projection_inside_cut(hit); + if (cont_id == -1) + return false; + if (m_part_selection.valid()) { + const std::vector& ign = *m_part_selection.get_ignored_contours_ptr(); + if (std::find(ign.begin(), ign.end(), cont_id) != ign.end()) + return false; + } + } + + + // recalculate hit to object's local position + Vec3d hit_d = hit; + hit_d -= mi->get_offset(); + hit_d[Z] -= sla_shift; + + // Return both the point and the facet normal. + pos = hit_d; + pos_world = hit; + + return true; +} + +void GLGizmoCut3D::clear_selection() +{ + m_selected.clear(); + m_selected_count = 0; +} + +void GLGizmoCut3D::reset_connectors() +{ + m_c->selection_info()->model_object()->cut_connectors.clear(); + update_raycasters_for_picking(); + clear_selection(); + check_and_update_connectors_state(); +} + +void GLGizmoCut3D::init_connector_shapes() +{ + for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug, CutConnectorType::Snap}) + for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism}) { + if (type == CutConnectorType::Dowel && style == CutConnectorStyle::Frustum) + continue; + for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { + if (type == CutConnectorType::Snap && shape != CutConnectorShape::Circle) + continue; + const CutConnectorAttributes attribs = { type, style, shape }; + indexed_triangle_set its = get_connector_mesh(attribs); + m_shapes[attribs].model.init_from(its); + m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + } +} + +void GLGizmoCut3D::update_connector_shape() +{ + CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; + + if (m_connector_type == CutConnectorType::Snap) { + indexed_triangle_set its = get_connector_mesh(attribs); + m_shapes[attribs].reset(); + m_shapes[attribs].model.init_from(its); + m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + + //const indexed_triangle_set its = get_connector_mesh(attribs); + //m_connector_mesh.clear(); + //m_connector_mesh = TriangleMesh(its); + } + + +} + +bool GLGizmoCut3D::cut_line_processing() const +{ + return !m_line_beg.isApprox(Vec3d::Zero()); +} + +void GLGizmoCut3D::discard_cut_line_processing() +{ + m_line_beg = m_line_end = Vec3d::Zero(); +} + +bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position) +{ + const Camera& camera = wxGetApp().plater()->get_camera(); + + Vec3d pt; + Vec3d dir; + MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, pt, dir); + dir.normalize(); + pt += dir; // Move the pt along dir so it is not clipped. + + if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { + m_line_beg = pt; + m_line_end = pt; + on_unregister_raycasters_for_picking(); + return true; + } + + if (cut_line_processing()) { + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + m_groove_editing = true; + reset_cut_by_contours(); + + m_line_end = pt; + if (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp) { + Vec3d line_dir = m_line_end - m_line_beg; + if (line_dir.norm() < 3.0) + return true; + + Vec3d cross_dir = line_dir.cross(dir).normalized(); + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir).toRotationMatrix(); + + const Vec3d new_plane_center = m_bb_center + cross_dir * cross_dir.dot(pt - m_bb_center); + // update transformed bb + const auto new_tbb = transformed_bounding_box(new_plane_center, m); + const GLVolume* first_volume = m_parent.get_selection().get_first_volume(); + Vec3d instance_offset = first_volume->get_instance_offset(); + instance_offset[Z] += first_volume->get_sla_shift_z(); + + const Vec3d trans_center_pos = m.inverse() * (new_plane_center - instance_offset) + new_tbb.center(); + if (new_tbb.contains(trans_center_pos)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); + m_transformed_bounding_box = new_tbb; + set_center(new_plane_center); + m_start_dragging_m = m_rotation_m = m; + m_ar_plane_center = m_plane_center; + } + + m_angle_arc.reset(); + discard_cut_line_processing(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + m_groove_editing = false; + reset_cut_by_contours(); + } + } + else if (action == SLAGizmoEventType::Moving) + this->set_dirty(); + return true; + } + return false; +} + +bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_position) +{ + if (!m_connectors_editing) + return false; + + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_position.cast(), pos, pos_world)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + unselect_all_connectors(); + + connectors.emplace_back(pos, m_rotation_m, + m_connector_size * 0.5f, m_connector_depth_ratio, + m_connector_size_tolerance * 0.5f, m_connector_depth_ratio_tolerance, + m_connector_angle, + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); + m_selected.push_back(true); + m_selected_count = 1; + assert(m_selected.size() == connectors.size()); + update_raycasters_for_picking(); + m_parent.set_as_dirty(); + check_and_update_connectors_state(); + + return true; + } + return false; +} + +bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) +{ + if (connectors.empty()) + return false; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); + + // remove connectors + for (int i = int(connectors.size()) - 1; i >= 0; i--) + if (m_selected[i]) + connectors.erase(connectors.begin() + i); + // remove selections + m_selected.erase(std::remove_if(m_selected.begin(), m_selected.end(), [](const auto& selected) { + return selected; }), m_selected.end()); + m_selected_count = 0; + + assert(m_selected.size() == connectors.size()); + update_raycasters_for_picking(); + m_parent.set_as_dirty(); + check_and_update_connectors_state(); + return true; +} + +void GLGizmoCut3D::select_connector(int idx, bool select) +{ + m_selected[idx] = select; + if (select) + ++m_selected_count; + else + --m_selected_count; +} + +bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool shift_down) +{ + if (m_hover_id >= m_connectors_group_id) { + if (alt_down) + select_connector(m_hover_id - m_connectors_group_id, false); + else { + if (!shift_down) + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + } + return true; + } + return false; +} + +void GLGizmoCut3D::process_selection_rectangle(CutConnectors &connectors) +{ + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + ModelObject* mo = m_c->selection_info()->model_object(); + int active_inst = m_c->selection_info()->get_active_instance(); + + // First collect positions of all the points in world coordinates. + Transformation trafo = mo->instances[active_inst]->get_transformation(); + trafo.set_offset(trafo.get_offset() + double(m_c->selection_info()->get_sla_shift()) * Vec3d::UnitZ()); + + std::vector points; + for (const CutConnector&connector : connectors) + points.push_back(connector.pos + trafo.get_offset()); + + // Now ask the rectangle which of the points are inside. + std::vector points_idxs = m_selection_rectangle.contains(points); + m_selection_rectangle.stop_dragging(); + + for (size_t idx : points_idxs) + select_connector(int(idx), rectangle_status == GLSelectionRectangle::EState::Select); +} + +bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (is_dragging() || m_connector_mode == CutConnectorMode::Auto) + return false; + + if ( (m_hover_id < 0 || m_hover_id == CutPlane) && shift_down && ! m_connectors_editing && + (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::Moving) ) + return process_cut_line(action, mouse_position); + + if (!m_keep_upper || !m_keep_lower) + return false; + + if (!m_connectors_editing) + return false; + + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + + if (action == SLAGizmoEventType::LeftDown) { + if (shift_down || alt_down) { + // left down with shift - show the selection rectangle: + if (m_hover_id == -1) + m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); + } + else + // If there is no selection and no hovering, add new point + if (m_hover_id == -1 && !shift_down && !alt_down) + if (!add_connector(connectors, mouse_position)) + m_ldown_mouse_position = mouse_position; + return true; + } + + if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle.is_dragging()) { + if ((m_ldown_mouse_position - mouse_position).norm() < 5.) + unselect_all_connectors(); + return is_selection_changed(alt_down, shift_down); + } + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + process_selection_rectangle(connectors); + return true; + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); + return true; + } + return false; + } + + if (action == SLAGizmoEventType::RightDown && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id < m_connectors_group_id) + return false; + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + return delete_selected_connectors(connectors); + } + + if (action == SLAGizmoEventType::Delete) + return delete_selected_connectors(connectors); + + if (action == SLAGizmoEventType::SelectAll) { + select_all_connectors(); + return true; + } + + return false; +} + +CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const { + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::ObjectClipper)); +} + +void GLGizmoCut3D::data_changed(bool is_serializing) +{ + update_bb(); + if (auto oc = m_c->object_clipper()) + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); +} + + + + +indexed_triangle_set GLGizmoCut3D::get_connector_mesh(CutConnectorAttributes connector_attributes) +{ + indexed_triangle_set connector_mesh; + + int sectorCount{ 1 }; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + sectorCount = 3; + break; + case CutConnectorShape::Square: + sectorCount = 4; + break; + case CutConnectorShape::Circle: + sectorCount = 360; + break; + case CutConnectorShape::Hexagon: + sectorCount = 6; + break; + default: + break; + } + + if (connector_attributes.type == CutConnectorType::Snap) + connector_mesh = its_make_snap(1.0, 1.0, m_snap_space_proportion, m_snap_bulge_proportion); + else if (connector_attributes.style == CutConnectorStyle::Prism) + connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); + else if (connector_attributes.type == CutConnectorType::Plug) + connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount)); + else + connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); + + return connector_mesh; +} + +void GLGizmoCut3D::apply_cut_connectors(ModelObject* mo, const std::string& connector_name) +{ + if (mo->cut_connectors.empty()) + return; + + using namespace Geometry; + + size_t connector_id = mo->cut_id.connectors_cnt(); + for (const CutConnector& connector : mo->cut_connectors) { + TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); + // Mesh will be centered when loading. + ModelVolume* new_volume = mo->add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); + + // Transform the new modifier to be aligned inside the instance + new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * + scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); + + new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; + new_volume->name = connector_name + "-" + std::to_string(++connector_id); + } + mo->cut_id.increase_connectors_cnt(mo->cut_connectors.size()); + + // delete all connectors + mo->cut_connectors.clear(); +} + + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 0d745fe3cb..2c36dbd2a5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -1,73 +1,390 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoCut_hpp_ #define slic3r_GLGizmoCut_hpp_ #include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLSelectionRectangle.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/I18N.hpp" #include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/ObjectID.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/CutUtils.hpp" +#include "imgui/imgui.h" namespace Slic3r { + +enum class CutConnectorType : int; +class ModelVolume; +class GLShaderProgram; +struct CutConnectorAttributes; + namespace GUI { +class Selection; -class GLGizmoCut : public GLGizmoBase +enum class SLAGizmoEventType : unsigned char; + +namespace CommonGizmosDataObjects { class ObjectClipper; } + +class GLGizmoCut3D : public GLGizmoBase { - static const double Offset; - static const double Margin; - static const std::array GrabberColor; - - double m_cut_z{ 0.0 }; - double m_max_z{ 0.0 }; - double m_start_z{ 0.0 }; - Vec3d m_drag_pos; - Vec3d m_drag_center; - bool m_keep_upper{ true }; - bool m_keep_lower{ true }; - bool m_rotate_lower{ false }; - // BBS: m_do_segment - bool m_cut_to_parts {false}; - bool m_do_segment{ false }; - double m_segment_smoothing_alpha{ 0.5 }; - int m_segment_number{ 5 }; - - struct CutContours - { - TriangleMesh mesh; - GLModel contours; - double cut_z{ 0.0 }; - Vec3d position{ Vec3d::Zero() }; - Vec3d shift{ Vec3d::Zero() }; - ObjectID object_id; - int instance_idx{ -1 }; + enum GrabberID { + X = 0, + Y, + Z, + CutPlane, + CutPlaneZRotation, + CutPlaneXMove, + CutPlaneYMove, + Count, }; - CutContours m_cut_contours; + Transform3d m_rotation_m{ Transform3d::Identity() }; + double m_snap_step{ 1.0 }; + int m_connectors_group_id; + + // archived values + Vec3d m_ar_plane_center { Vec3d::Zero() }; + Transform3d m_start_dragging_m{ Transform3d::Identity() }; + + Vec3d m_plane_center{ Vec3d::Zero() }; + // data to check position of the cut palne center on gizmo activation + Vec3d m_min_pos{ Vec3d::Zero() }; + Vec3d m_max_pos{ Vec3d::Zero() }; + Vec3d m_bb_center{ Vec3d::Zero() }; + Vec3d m_center_offset{ Vec3d::Zero() }; + + BoundingBoxf3 m_bounding_box; + BoundingBoxf3 m_transformed_bounding_box; + + // values from RotationGizmo + double m_radius{ 0.0 }; + double m_grabber_radius{ 0.0 }; + double m_grabber_connection_len{ 0.0 }; + Vec3d m_cut_plane_start_move_pos {Vec3d::Zero()}; + + double m_snap_coarse_in_radius{ 0.0 }; + double m_snap_coarse_out_radius{ 0.0 }; + double m_snap_fine_in_radius{ 0.0 }; + double m_snap_fine_out_radius{ 0.0 }; + + // dragging angel in hovered axes + double m_angle{ 0.0 }; + + TriangleMesh m_connector_mesh; + // workaround for using of the clipping plane normal + Vec3d m_clp_normal{ Vec3d::Ones() }; + + Vec3d m_line_beg{ Vec3d::Zero() }; + Vec3d m_line_end{ Vec3d::Zero() }; + + Vec2d m_ldown_mouse_position{ Vec2d::Zero() }; + + GLModel m_grabber_connection; + GLModel m_cut_line; + + PickingModel m_plane; + PickingModel m_sphere; + PickingModel m_cone; + PickingModel m_cube; + std::map m_shapes; + std::vector> m_raycasters; + + GLModel m_circle; + GLModel m_scale; + GLModel m_snap_radii; + GLModel m_reference_radius; + GLModel m_angle_arc; + + Vec3d m_old_center; + Vec3d m_cut_normal; + + struct InvalidConnectorsStatistics + { + unsigned int outside_cut_contour; + unsigned int outside_bb; + bool is_overlap; + + void invalidate() { + outside_cut_contour = 0; + outside_bb = 0; + is_overlap = false; + } + } m_info_stats; + + bool m_keep_upper{ true }; + bool m_keep_lower{ true }; + bool m_keep_as_parts{ false }; + bool m_place_on_cut_upper{ true }; + bool m_place_on_cut_lower{ false }; + bool m_rotate_upper{ false }; + bool m_rotate_lower{ false }; + + // Input params for cut with tongue and groove + Cut::Groove m_groove; + bool m_groove_editing { false }; + + bool m_is_slider_editing_done { false }; + + // Input params for cut with snaps + float m_snap_bulge_proportion{ 0.15f }; + float m_snap_space_proportion{ 0.3f }; + + bool m_hide_cut_plane{ false }; + bool m_connectors_editing{ false }; + bool m_cut_plane_as_circle{ false }; + + float m_connector_depth_ratio{ 3.f }; + float m_connector_size{ 2.5f }; + float m_connector_angle{ 0.f }; + + float m_connector_depth_ratio_tolerance{ 0.1f }; + float m_connector_size_tolerance{ 0.f }; + + float m_label_width{ 0.f }; + float m_control_width{ 200.f }; + double m_editing_window_width; + bool m_imperial_units{ false }; + + float m_contour_width{ 0.4f }; + float m_cut_plane_radius_koef{ 1.5f }; + + mutable std::vector m_selected; // which pins are currently selected + int m_selected_count{ 0 }; + + GLSelectionRectangle m_selection_rectangle; + + std::vector m_invalid_connectors_idxs; + bool m_was_cut_plane_dragged { false }; + bool m_was_contour_selected { false }; + + // Vertices of the groove used to detection if groove is valid + std::vector m_groove_vertices; + + class PartSelection { + public: + PartSelection() = default; + PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc); + PartSelection(const ModelObject* mo, int instance_idx_in); + ~PartSelection() { m_model.clear_objects(); } + + struct Part { + GLModel glmodel; + MeshRaycaster raycaster; + bool selected; + bool is_modifier; + }; + + void render(const Vec3d* normal, GLModel& sphere_model); + void toggle_selection(const Vec2d& mouse_pos); + void turn_over_selection(); + ModelObject* model_object() { return m_model.objects.front(); } + bool valid() const { return m_valid; } + bool is_one_object() const; + const std::vector& parts() const { return m_parts; } + const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } + + std::vector get_cut_parts(); + + private: + Model m_model; + int m_instance_idx; + std::vector m_parts; + bool m_valid = false; + std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below + std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) + + std::vector m_contour_points; // Debugging + std::vector> m_debug_pts; // Debugging + + void add_object(const ModelObject* object); + }; + + PartSelection m_part_selection; + + std::vector> m_shortcuts_cut; + std::vector> m_shortcuts_connector; + + enum class CutMode { + cutPlanar + , cutTongueAndGroove + //, cutGrig + //,cutRadial + //,cutModular + }; + + enum class CutConnectorMode { + Auto + , Manual + }; + + std::vector m_modes; + size_t m_mode{ size_t(CutMode::cutPlanar) }; + + std::vector m_connector_modes; + CutConnectorMode m_connector_mode{ CutConnectorMode::Manual }; + + std::vector m_connector_types; + CutConnectorType m_connector_type; + + std::vector m_connector_styles; + int m_connector_style; + + std::vector m_connector_shapes; + int m_connector_shape_id; + + std::vector m_axis_names; + + std::map m_part_orientation_names; + + std::map m_labels_map; public: - GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - - double get_cut_z() const { return m_cut_z; } - void set_cut_z(double cut_z); + GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; + bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_contours = true); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + + bool is_in_editing_mode() const override { return m_connectors_editing; } + bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); } + bool is_looking_forward() const; + + /// + /// Drag of plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void shift_cut(double delta); + void rotate_vec3d_around_plane_center(Vec3d&vec); + void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); + void update_clipper(); + void invalidate_cut_plane(); + + BoundingBoxf3 bounding_box() const; + BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, const Transform3d& rotation_m = Transform3d::Identity()) const; protected: - virtual bool on_init() override; - virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual std::string on_get_name() const override; - virtual void on_set_state() override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; - virtual void on_update(const UpdateData& data) override; - virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_render_input_window(float x, float y, float bottom_limit) override; + bool on_init() override; + void on_load(cereal::BinaryInputArchive&ar) override; + void on_save(cereal::BinaryOutputArchive&ar) const override; + std::string on_get_name() const override; + void on_set_state() override; + CommonGizmosDataID on_get_requirements() const override; + void on_set_hover_id() override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const; + void dragging_grabber_move(const GLGizmoBase::UpdateData &data); + void dragging_grabber_rotation(const GLGizmoBase::UpdateData &data); + void dragging_connector(const GLGizmoBase::UpdateData &data); + void on_dragging(const UpdateData&data) override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render() override; + + void render_debug_input_window(float x); + void unselect_all_connectors(); + void select_all_connectors(); + void apply_selected_connectors(std::function apply_fn); + void render_connectors_input_window(CutConnectors &connectors, float x, float y, float bottom_limit); + void render_build_size(); + void reset_cut_plane(); + void set_connectors_editing(bool connectors_editing); + void flip_cut_plane(); + void process_contours(); + void reset_cut_by_contours(); + void render_flip_plane_button(bool disable_pred = false); + void add_vertical_scaled_interval(float interval); + void add_horizontal_scaled_interval(float interval); + void add_horizontal_shift(float shift); + void render_color_marker(float size, const ImU32& color); + void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance); + void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val); + bool render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val); + void render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val); + void render_cut_plane_input_window(CutConnectors &connectors, float x, float y, float bottom_limit); + void init_input_window_data(CutConnectors &connectors); + void render_input_window_warning() const; + bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position); + bool delete_selected_connectors(CutConnectors&connectors); + void select_connector(int idx, bool select); + bool is_selection_changed(bool alt_down, bool shift_down); + void process_selection_rectangle(CutConnectors &connectors); + + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; + void update_raycasters_for_picking(); + void set_volumes_picking_state(bool state); + void update_raycasters_for_picking_transform(); + + void update_plane_model(); + + void on_render_input_window(float x, float y, float bottom_limit) override; + void show_tooltip_information(float x, float y); + + bool wants_enter_leave_snapshots() const override { return true; } + std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); } + std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); } + + void data_changed(bool is_serializing) override; + Transform3d get_cut_matrix(const Selection& selection); private: - void perform_cut(const Selection& selection); - double calc_projection(const Linef3& mouse_ray) const; - BoundingBoxf3 bounding_box() const; - void update_contours(); + void set_center(const Vec3d¢er, bool update_tbb = false); + void switch_to_mode(size_t new_mode); + bool render_cut_mode_combo(); + bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); + bool render_double_input(const std::string& label, double& value_in); + bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f); + void render_move_center_input(int axis); + void render_connect_mode_radio_button(CutConnectorMode mode); + bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; + bool render_connect_type_radio_button(CutConnectorType type); + bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); + bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); + void render_connectors(); + + bool can_perform_cut() const; + bool has_valid_groove() const; + bool has_valid_contour() const; + void apply_connectors_in_model(ModelObject* mo, int &dowels_count); + bool cut_line_processing() const; + void discard_cut_line_processing(); + + void apply_color_clip_plane_colors(); + void render_cut_plane(); + static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); + void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); + void render_rotation_snapping(GrabberID axis, const ColorRGBA& color); + void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0); + void render_cut_plane_grabbers(); + void render_cut_line(); + void perform_cut(const Selection&selection); + void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false); + void update_bb(); + void init_picking_models(); + void init_rendering_items(); + void render_clipper_cut(); + void clear_selection(); + void reset_connectors(); + void init_connector_shapes(); + void update_connector_shape(); + void validate_connector_settings(); + bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); + void check_and_update_connectors_state(); + + void toggle_model_objects_visibility(); + + indexed_triangle_set its_make_groove_plane(); + + indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); + void apply_cut_connectors(ModelObject* mo, const std::string& connector_name); }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp index b96be246a3..90b274decd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp @@ -32,9 +32,9 @@ std::string GLGizmoFaceDetector::on_get_name() const void GLGizmoFaceDetector::on_render() { - if (m_iva.has_VBOs()) { - ::glColor4f(0.f, 0.f, 1.f, 0.4f); - m_iva.render(); + if (model.is_initialized()) { + model.set_color({0.f, 0.f, 1.f, 0.4f}); + model.render(); } } @@ -72,7 +72,7 @@ void GLGizmoFaceDetector::on_render_input_window(float x, float y, float bottom_ void GLGizmoFaceDetector::on_set_state() { if (get_state() == On) { - m_iva.release_geometry(); + model.reset(); display_exterior_face(); } } @@ -94,7 +94,10 @@ void GLGizmoFaceDetector::perform_recognition(const Selection& selection) void GLGizmoFaceDetector::display_exterior_face() { int cnt = 0; - m_iva.release_geometry(); + model.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; const ModelObjectPtrs& objects = wxGetApp().model().objects; for (ModelObject* mo : objects) { @@ -110,19 +113,15 @@ void GLGizmoFaceDetector::display_exterior_face() continue; for (int i = 0; i < 3; ++i) { - m_iva.push_geometry(double(mv_its.vertices[facet_vert_idxs[i]](0)), - double(mv_its.vertices[facet_vert_idxs[i]](1)), - double(mv_its.vertices[facet_vert_idxs[i]](2)), - 0., 0., 1.); + init_data.add_vertex((Vec3f) mv_its.vertices[facet_vert_idxs[i]].cast(), Vec3f{0.0f, 0.0f, 1.0f}); } - m_iva.push_triangle(cnt, cnt + 1, cnt + 2); + init_data.add_uint_triangle(cnt, cnt + 1, cnt + 2); cnt += 3; } } } - - m_iva.finalize_geometry(true); + model.init_from(std::move(init_data)); } CommonGizmosDataID GLGizmoFaceDetector::on_get_requirements() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp index c20cf4c209..c028a3f2b2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp @@ -28,7 +28,7 @@ private: void perform_recognition(const Selection& selection); void display_exterior_face(); - GLIndexedVertexArray m_iva; + GUI::GLModel model; double m_sample_interval = {0.5}; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index c095c0915e..1a6ad83ca9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -105,7 +105,7 @@ bool GLGizmoFdmSupports::on_init() return true; } -void GLGizmoFdmSupports::render_painter_gizmo() const +void GLGizmoFdmSupports::render_painter_gizmo() { const Selection& selection = m_parent.get_selection(); @@ -116,8 +116,8 @@ void GLGizmoFdmSupports::render_painter_gizmo() const //BBS: draw support volumes if (m_volume_ready && m_support_volume && (m_edit_state != state_generating)) { - //m_support_volume->set_render_color(); - ::glColor4f(0.f, 0.7f, 0.f, 0.7f); + // TODO: FIXME + m_support_volume->set_render_color({0.f, 0.7f, 0.f, 0.7f}); m_support_volume->render(); } @@ -177,8 +177,12 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const if (is_left_handed) glsafe(::glFrontFace(GL_CW)); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); float normal_z = -::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg)); Matrix3f normal_matrix = static_cast(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast()); @@ -188,9 +192,8 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const shader->set_uniform("slope.actived", m_parent.is_using_slope()); shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix); shader->set_uniform("slope.normal_z", normal_z); - m_triangle_selectors[mesh_id]->render(m_imgui); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); - glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); } @@ -427,7 +430,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -441,7 +444,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position(clp_dist, true); + if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); } ImGui::Separator(); @@ -640,7 +643,7 @@ void GLGizmoFdmSupports::update_from_model_object(bool first_update) m_volume_timestamps.clear(); int volume_id = -1; - std::vector> ebt_colors; + std::vector ebt_colors; ebt_colors.push_back(GLVolume::NEUTRAL_COLOR); ebt_colors.push_back(TriangleSelectorGUI::enforcers_color); ebt_colors.push_back(TriangleSelectorGUI::blockers_color); @@ -894,13 +897,16 @@ void GLGizmoFdmSupports::run_thread() print->set_status(100, L("Support Generated")); goto _finished; } + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; for (const SupportLayer *support_layer : m_print_instance.print_object->support_layers()) { for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) { - _3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume); + _3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, init_data); } } + m_support_volume->model.init_from(std::move(init_data)); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%"; print->set_status(100, L("Support Generated")); @@ -926,7 +932,6 @@ _finished: void GLGizmoFdmSupports::generate_support_volume() { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",before finalize_geometry"; - m_support_volume->indexed_vertex_array.finalize_geometry(m_parent.is_initialized()); std::unique_lock lck(m_mutex); m_volume_ready = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 6960a81dc6..306d1f13bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -5,6 +5,7 @@ //BBS #include "libslic3r/Print.hpp" #include "libslic3r/ObjectID.hpp" +#include "slic3r/GUI/3DScene.hpp" #include @@ -15,7 +16,7 @@ class GLGizmoFdmSupports : public GLGizmoPainterBase public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - void render_painter_gizmo() const override; + void render_painter_gizmo() override; //BBS: add edit state enum EditState { @@ -39,7 +40,7 @@ protected: std::string get_gizmo_entering_text() const override { return "Entering Paint-on supports"; } std::string get_gizmo_leaving_text() const override { return "Leaving Paint-on supports"; } - std::string get_action_snapshot_name() override { return "Paint-on supports editing"; } + std::string get_action_snapshot_name() const override { return "Paint-on supports editing"; } // BBS wchar_t m_current_tool = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 4ad8136bba..e4a4f895e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -1,6 +1,11 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoFlatten.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" @@ -16,14 +21,43 @@ namespace GUI { GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_normal(Vec3d::Zero()) - , m_starting_center(Vec3d::Zero()) +{} + +bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { + if (mouse_event.LeftDown()) { + if (m_hover_id != -1) { + Selection &selection = m_parent.get_selection(); + if (selection.is_single_full_instance()) { + // Rotate the object so the normal points downward: + selection.flattening_rotate(m_planes[m_hover_id].normal); + m_parent.do_rotate(L("Gizmo-Place on Face")); + wxGetApp().obj_manipul()->set_dirty(); + } + return true; + } + } + else if (mouse_event.LeftUp()) + return m_hover_id != -1; + + return false; +} + +void GLGizmoFlatten::data_changed(bool is_serializing) +{ + const Selection & selection = m_parent.get_selection(); + const ModelObject *model_object = nullptr; + int instance_id = -1; + if (selection.is_single_full_instance() || + selection.is_from_single_object() ) { + model_object = selection.get_model()->objects[selection.get_object_idx()]; + instance_id = selection.get_instance_idx(); + } + set_flattening_data(model_object, instance_id); } bool GLGizmoFlatten::on_init() { - // BBS m_shortcut_key = WXK_CONTROL_F; return true; } @@ -49,77 +83,71 @@ bool GLGizmoFlatten::on_is_activable() const return m_parent.get_selection().is_single_full_instance(); } -void GLGizmoFlatten::on_start_dragging() -{ - if (m_hover_id != -1) { - assert(m_planes_valid); - m_normal = m_planes[m_hover_id].normal; - m_starting_center = m_parent.get_selection().get_bounding_box().center(); - } -} - void GLGizmoFlatten::on_render() { const Selection& selection = m_parent.get_selection(); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); if (selection.is_single_full_instance()) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z())); - glsafe(::glMultMatrixd(m.data())); + const Transform3d& inst_matrix = selection.get_first_volume()->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d model_matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst_matrix; + const Transform3d view_model_matrix = camera.get_view_matrix() * model_matrix; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { - if (i == m_hover_id) - glsafe(::glColor4fv(GLGizmoBase::FLATTEN_HOVER_COLOR.data())); - else - glsafe(::glColor4fv(GLGizmoBase::FLATTEN_COLOR.data())); - - if (m_planes[i].vbo.has_VBOs()) - m_planes[i].vbo.render(); + m_planes[i].vbo.model.set_color(i == m_hover_id ? GLGizmoBase::FLATTEN_HOVER_COLOR : GLGizmoBase::FLATTEN_COLOR); + m_planes[i].vbo.model.render(); } - glsafe(::glPopMatrix()); } glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); + shader->stop_using(); } -void GLGizmoFlatten::on_render_for_picking() +void GLGizmoFlatten::on_register_raycasters_for_picking() { - const Selection& selection = m_parent.get_selection(); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_BLEND)); + assert(m_planes_casters.empty()); + + if (!m_planes.empty()) { + const Selection& selection = m_parent.get_selection(); + const Transform3d matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * + selection.get_first_volume()->get_instance_transformation().get_matrix(); - if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z())); - glsafe(::glMultMatrixd(m.data())); - if (this->is_plane_update_necessary()) - update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { - glsafe(::glColor4fv(picking_color_component(i).data())); - m_planes[i].vbo.render(); + m_planes_casters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_planes[i].vbo.mesh_raycaster, matrix)); } - glsafe(::glPopMatrix()); } - - glsafe(::glEnable(GL_CULL_FACE)); } -void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) +void GLGizmoFlatten::on_unregister_raycasters_for_picking() { - m_starting_center = Vec3d::Zero(); - if (model_object != m_old_model_object) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); + m_planes_casters.clear(); +} + +void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object, int instance_id) +{ + if (model_object != m_old_model_object || instance_id != m_old_instance_id) { m_planes.clear(); - m_planes_valid = false; + on_unregister_raycasters_for_picking(); } } @@ -136,6 +164,7 @@ void GLGizmoFlatten::update_planes() } ch = ch.convex_hull_3d(); m_planes.clear(); + on_unregister_raycasters_for_picking(); const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true); // Following constants are used for discarding too small polygons. @@ -196,9 +225,7 @@ void GLGizmoFlatten::update_planes() } // Let's prepare transformation of the normal vector from mesh to instance coordinates. - Geometry::Transformation t(inst_matrix); - Vec3d scaling = t.get_scaling_factor(); - t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); + const Matrix3d normal_matrix = inst_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); // Now we'll go through all the polygons, transform the points into xy plane to process them: for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { @@ -206,7 +233,7 @@ void GLGizmoFlatten::update_planes() const Vec3d& normal = m_planes[polygon_id].normal; // transform the normal according to the instance matrix: - Vec3d normal_transformed = t.get_matrix() * normal; + const Vec3d normal_transformed = normal_matrix * normal; // We are going to rotate about z and y to flatten the plane Eigen::Quaterniond q; @@ -219,7 +246,7 @@ void GLGizmoFlatten::update_planes() // And yes, it is a nasty thing to do. Whoever has time is free to refactor. Vec3d bb_size = BoundingBoxf3(polygon).size(); float sf = std::min(1./bb_size(0), 1./bb_size(1)); - Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); + Transform3d tr = Geometry::scale_transform({ sf, sf, 1.f }); polygon = transform(polygon, tr); polygon = Slic3r::Geometry::convex_hull(polygon); polygon = transform(polygon, tr.inverse()); @@ -324,34 +351,46 @@ void GLGizmoFlatten::update_planes() m_first_instance_scale = mo->instances.front()->get_scaling_factor(); m_first_instance_mirror = mo->instances.front()->get_mirror(); m_old_model_object = mo; + m_old_instance_id = m_c->selection_info()->get_active_instance(); // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - plane.vbo.reserve(plane.vertices.size()); - for (const auto& vert : plane.vertices) - plane.vbo.push_geometry(vert, plane.normal); - for (size_t i=1; i()); + } + for (size_t i = 1; i < plane.vertices.size() - 1; ++i) { + its.indices.emplace_back(0, i, i + 1); // triangle fan + } + + plane.vbo.model.init_from(its); + if (Geometry::Transformation(inst_matrix).is_left_handed()) { + // we need to swap face normals in case the object is mirrored + // for the raycaster to work properly + for (stl_triangle_vertex_indices& face : its.indices) { + if (its_face_normal(its, face).cast().dot(plane.normal) < 0.0) + std::swap(face[1], face[2]); + } + } + plane.vbo.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + // vertices are no more needed, clear memory + plane.vertices = std::vector(); } - m_planes_valid = true; + on_register_raycasters_for_picking(); } - bool GLGizmoFlatten::is_plane_update_necessary() const { const ModelObject* mo = m_c->selection_info()->model_object(); if (m_state != On || ! mo || mo->instances.empty()) return false; - if (! m_planes_valid || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) + if (m_planes.empty() || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) return true; // We want to recalculate when the scale changes - some planes could (dis)appear. @@ -367,13 +406,5 @@ bool GLGizmoFlatten::is_plane_update_necessary() const return false; } -Vec3d GLGizmoFlatten::get_flattening_normal() const -{ - Vec3d out = m_normal; - m_normal = Vec3d::Zero(); - m_starting_center = Vec3d::Zero(); - return out; -} - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index ab3c2c7bab..027480dbee 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -1,9 +1,13 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoFlatten_hpp_ #define slic3r_GLGizmoFlatten_hpp_ #include "GLGizmoBase.hpp" -#include "slic3r/GUI/3DScene.hpp" - +#include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/MeshUtils.hpp" namespace Slic3r { @@ -18,13 +22,13 @@ class GLGizmoFlatten : public GLGizmoBase // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. private: - mutable Vec3d m_normal; struct PlaneData { std::vector vertices; // should be in fact local in update_planes() - GLIndexedVertexArray vbo; + PickingModel vbo; Vec3d normal; float area; + int picking_id{ -1 }; }; // This holds information to decide whether recalculation is necessary: @@ -34,10 +38,9 @@ private: Vec3d m_first_instance_mirror; std::vector m_planes; - bool m_planes_valid = false; - mutable Vec3d m_starting_center; + std::vector> m_planes_casters; const ModelObject* m_old_model_object = nullptr; - std::vector instances_matrices; + int m_old_instance_id{ -1 }; void update_planes(); bool is_plane_update_necessary() const; @@ -45,18 +48,25 @@ private: public: GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - void set_flattening_data(const ModelObject* model_object); - Vec3d get_flattening_normal() const; + void set_flattening_data(const ModelObject* model_object, int instance_id); + + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + void data_changed(bool is_serializing) override; protected: - virtual bool on_init() override; - virtual std::string on_get_name() const override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; - virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_set_state() override; - virtual CommonGizmosDataID on_get_requirements() const override; + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_render() override; + void on_register_raycasters_for_picking() override; + void on_unregister_raycasters_for_picking() override; + void on_set_state() override; + CommonGizmosDataID on_get_requirements() const override; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 39bc98d7fb..03e12c22db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -20,7 +20,6 @@ namespace GUI { GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); } @@ -63,6 +62,9 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) void GLGizmoHollow::on_render() { + if (!m_cylinder.is_initialized()) + m_cylinder.init_from(its_make_cylinder(1.0, 1.0)); + const Selection& selection = m_parent.get_selection(); const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info(); @@ -87,98 +89,75 @@ void GLGizmoHollow::on_render() glsafe(::glDisable(GL_BLEND)); } - -void GLGizmoHollow::on_render_for_picking() +void GLGizmoHollow::render_points(const Selection& selection, bool picking) { - const Selection& selection = m_parent.get_selection(); -//#if ENABLE_RENDER_PICKING_PASS -// m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); -//#endif + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; - glsafe(::glEnable(GL_DEPTH_TEST)); - render_points(selection, true); -} - -void GLGizmoHollow::render_points(const Selection& selection, bool picking) const -{ - GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); - if (shader) - shader->start_using(); - ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); + shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); - const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * vol->get_instance_transformation().get_matrix(); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); - glsafe(::glMultMatrixd(instance_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); - std::array render_color; + shader->set_uniform("projection_matrix", projection_matrix); + + ColorRGBA render_color; const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; - size_t cache_size = drain_holes.size(); + const size_t cache_size = drain_holes.size(); for (size_t i = 0; i < cache_size; ++i) { const sla::DrainHole& drain_hole = drain_holes[i]; - const bool& point_selected = m_selected[i]; + const bool point_selected = m_selected[i]; if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; // First decide about the color of the point. - if (picking) { - std::array color = picking_color_component(i); - render_color = color; - } + if (picking) + render_color = picking_color_component(i); else { - if (size_t(m_hover_id) == i) { - render_color = {0.f, 1.f, 1.f, 1.f}; - } + if (size_t(m_hover_id) == i) + render_color = ColorRGBA::CYAN(); else if (m_c->hollowed_mesh() && i < m_c->hollowed_mesh()->get_drainholes().size() && m_c->hollowed_mesh()->get_drainholes()[i].failed) { - render_color = {1.f, 0.f, 0.f, .5f}; - } - else { // neigher hover nor picking - - render_color[0] = point_selected ? 1.0f : 1.f; - render_color[1] = point_selected ? 0.3f : 1.f; - render_color[2] = point_selected ? 0.3f : 1.f; - render_color[3] = 0.5f; + render_color = { 1.0f, 0.0f, 0.0f, 0.5f }; } + else // neither hover nor picking + render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); } - const_cast(&m_vbo_cylinder)->set_color(-1, render_color); + m_cylinder.set_color(render_color); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + const Transform3d hole_matrix = Geometry::assemble_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; if (vol->is_left_handed()) glFrontFace(GL_CW); // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - m_vbo_cylinder.render(); - glsafe(::glPopMatrix()); + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d model_matrix = instance_matrix * hole_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform(-drain_hole.height * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_cylinder.render(); if (vol->is_left_handed()) glFrontFace(GL_CCW); - glsafe(::glPopMatrix()); } - - glsafe(::glPopMatrix()); } - - bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { if (m_c->object_clipper()->get_position() == 0.) @@ -544,14 +523,11 @@ RENDER_AGAIN: } m_imgui->disabled_begin(! m_enable_hollowing); - float max_tooltip_width = ImGui::GetFontSize() * 20.0f; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("offset")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - settings_sliders_left); - m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm"); - if (m_imgui->get_last_slider_status().hovered) - m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); + m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm", 1.0f, true, _L(opts[0].second->tooltip)); bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider bool slider_edited =m_imgui->get_last_slider_status().edited; // someone is dragging the slider @@ -561,9 +537,7 @@ RENDER_AGAIN: ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("quality")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); - m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f"); - if (m_imgui->get_last_slider_status().hovered) - m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); + m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f", 1.0f, true, _L(opts[1].second->tooltip)); slider_clicked |= m_imgui->get_last_slider_status().clicked; slider_edited |= m_imgui->get_last_slider_status().edited; @@ -574,9 +548,7 @@ RENDER_AGAIN: ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("closing_distance")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); - m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); - if (m_imgui->get_last_slider_status().hovered) - m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); + m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm", 1.0f, true, _L(opts[2].second->tooltip)); slider_clicked |= m_imgui->get_last_slider_status().clicked; slider_edited |= m_imgui->get_last_slider_status().edited; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 2cf08de2a0..e60ece0d52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -40,15 +40,15 @@ private: bool on_init() override; void on_update(const UpdateData& data) override; void on_render() override; - void on_render_for_picking() override; - void render_points(const Selection& selection, bool picking = false) const; + void render_points(const Selection& selection, bool picking = false); void hollow_mesh(bool postpone_error_messages = false); bool unsaved_changes() const; ObjectID m_old_mo_id = -1; - GLModel m_vbo_cylinder; + GLModel m_cylinder; + float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp new file mode 100644 index 0000000000..0c8b42fe16 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -0,0 +1,2068 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "GLGizmoMeasure.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp" + + +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/MeasureUtils.hpp" + +#include + +#include + +#include + +#include + +#include + +namespace Slic3r { +namespace GUI { + +static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; +static const Slic3r::ColorRGBA NEUTRAL_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}; +static const Slic3r::ColorRGBA HOVER_COLOR = ColorRGBA::GREEN(); + +static const int POINT_ID = 100; +static const int EDGE_ID = 200; +static const int CIRCLE_ID = 300; +static const int PLANE_ID = 400; +static const int SEL_SPHERE_1_ID = 501; +static const int SEL_SPHERE_2_ID = 502; + +static const float TRIANGLE_BASE = 10.0f; +static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; + +static const std::string CTRL_STR = +#ifdef __APPLE__ +"⌘" +#else +"Ctrl" +#endif //__APPLE__ +; + +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +} + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +} + +static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Measure::SurfaceFeatureType::Undef: { return ("No feature"); } + case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return _u8L("Plane"); } + } +} + +static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + return ret; +} + +static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Center of edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Center of circle"); break; } + default: { assert(false); break; } + } + return ret; +} + +static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector& triangle_indices) +{ + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_indices(3 * triangle_indices.size()); + init_data.reserve_vertices(3 * triangle_indices.size()); + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + return init_data; +} + +static GLModel::Geometry init_torus_data(unsigned int primary_resolution, unsigned int secondary_resolution, const Vec3f& center, + float radius, float thickness, const Vec3f& model_axis, const Transform3f& world_trafo) +{ + const unsigned int torus_sector_count = std::max(4, primary_resolution); + const unsigned int section_sector_count = std::max(4, secondary_resolution); + const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); + const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torus_sector_count * section_sector_count); + data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); + + // vertices + const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast()).cast() * + Eigen::Quaternion::FromTwoVectors(Vec3f::UnitZ(), model_axis); + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const float section_angle = torus_sector_step * i; + const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f); + const Vec3f local_section_center = radius * radius_dir; + const Vec3f world_section_center = local_to_world_matrix * local_section_center; + const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized(); + const Vec3f world_section_normal = (Vec3f)(local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized(); + const Vec3f base_v = thickness * radius_dir; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v; + data.add_vertex(world_section_center + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const unsigned int ii = i * section_sector_count; + const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const unsigned int j_next = (j + 1) % section_sector_count; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} + +static bool is_feature_with_center(const Measure::SurfaceFeature& feature) +{ + const Measure::SurfaceFeatureType type = feature.get_type(); + return (type == Measure::SurfaceFeatureType::Circle || (type == Measure::SurfaceFeatureType::Edge && feature.get_extra_point().has_value())); +} + +static Vec3d get_feature_offset(const Measure::SurfaceFeature& feature) +{ + Vec3d ret; + switch (feature.get_type()) + { + case Measure::SurfaceFeatureType::Circle: + { + const auto [center, radius, normal] = feature.get_circle(); + ret = center; + break; + } + case Measure::SurfaceFeatureType::Edge: + { + std::optional p = feature.get_extra_point(); + assert(p.has_value()); + ret = *p; + break; + } + case Measure::SurfaceFeatureType::Point: + { + ret = feature.get_point(); + break; + } + default: { assert(false); } + } + + return ret; +} + +class TransformHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { + return world_matrix * model; + } + + static Vec4d world_to_clip(const Vec3d& world, const Matrix4d& projection_view_matrix) { + return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); + } + + static Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + } + + static Vec2d ndc_to_ss(const Vec3d& ndc, const std::array& viewport) { + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h }; + }; + + static Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); + } + + static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); + } + + static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); + } + + static Vec2d world_to_ss(const Vec3d& world, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(world, projection_view_matrix)), viewport); + } + + static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix; + } + + static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; + } + +private: + static void update(const std::array& viewport) { + if (s_cache.viewport == viewport) + return; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; + } +}; + +TransformHelper::Cache TransformHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; + +GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) +: GLGizmoBase(parent, icon_filename, sprite_id) +{ + GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(sphere_geometry.get_as_indexed_triangle_set())); + m_sphere.model.init_from(std::move(sphere_geometry)); + + GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(cylinder_geometry.get_as_indexed_triangle_set())); + m_cylinder.model.init_from(std::move(cylinder_geometry)); +} + +bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) +{ + m_mouse_pos = { double(mouse_event.GetX()), double(mouse_event.GetY()) }; + + if (mouse_event.Moving()) { + // only for sure + m_mouse_left_down = false; + return false; + } + else if (mouse_event.Dragging()) { + // Enable/Disable panning/rotating the 3D scene + // Ctrl is pressed or the mouse is not hovering a selected volume + bool unlock_dragging = mouse_event.CmdDown() || (m_hover_id == -1 && !m_parent.get_selection().contains_volume(m_parent.get_first_hover_volume_idx())); + // mode is not center selection or mouse is not hovering a center + unlock_dragging &= !mouse_event.ShiftDown() || (m_hover_id != SEL_SPHERE_1_ID && m_hover_id != SEL_SPHERE_2_ID && m_hover_id != POINT_ID); + return !unlock_dragging; + } + else if (mouse_event.LeftDown()) { + // let the event pass through to allow panning/rotating the 3D scene + if (mouse_event.CmdDown()) + return false; + + if (m_hover_id != -1) { + m_mouse_left_down = true; + + auto detect_current_item = [this]() { + SelectedFeatures::Item item; + if (m_hover_id == SEL_SPHERE_1_ID) { + if (m_selected_features.first.is_center) + // mouse is hovering over a selected center + item = { true, m_selected_features.first.source, { Measure::SurfaceFeature(get_feature_offset(*m_selected_features.first.source)) } }; + else if (is_feature_with_center(*m_selected_features.first.feature)) + // mouse is hovering over a unselected center + item = { true, m_selected_features.first.feature, { Measure::SurfaceFeature(get_feature_offset(*m_selected_features.first.feature)) } }; + else + // mouse is hovering over a point + item = m_selected_features.first; + } + else if (m_hover_id == SEL_SPHERE_2_ID) { + if (m_selected_features.second.is_center) + // mouse is hovering over a selected center + item = { true, m_selected_features.second.source, { Measure::SurfaceFeature(get_feature_offset(*m_selected_features.second.source)) } }; + else if (is_feature_with_center(*m_selected_features.second.feature)) + // mouse is hovering over a center + item = { true, m_selected_features.second.feature, { Measure::SurfaceFeature(get_feature_offset(*m_selected_features.second.feature)) } }; + else + // mouse is hovering over a point + item = m_selected_features.second; + } + else { + switch (m_mode) + { + case EMode::FeatureSelection: { item = { false, m_curr_feature, m_curr_feature }; break; } + case EMode::PointSelection: { item = { false, m_curr_feature, Measure::SurfaceFeature(*m_curr_point_on_feature_position) }; break; } + } + } + return item; + }; + + auto requires_sphere_raycaster_for_picking = [this](const SelectedFeatures::Item& item) { + if (m_mode == EMode::PointSelection || item.feature->get_type() == Measure::SurfaceFeatureType::Point) + return true; + else if (m_mode == EMode::FeatureSelection) { + if (is_feature_with_center(*item.feature)) + return true; + } + return false; + }; + + if (m_selected_features.first.feature.has_value()) { + const SelectedFeatures::Item item = detect_current_item(); + if (m_selected_features.first != item) { + bool processed = false; + if (item.is_center) { + if (item.source == m_selected_features.first.feature) { + // switch 1st selection from feature to its center + m_selected_features.first = item; + processed = true; + } + else if (item.source == m_selected_features.second.feature) { + // switch 2nd selection from feature to its center + m_selected_features.second = item; + processed = true; + } + } + else if (is_feature_with_center(*item.feature)) { + if (m_selected_features.first.is_center && m_selected_features.first.source == item.feature) { + // switch 1st selection from center to its feature + m_selected_features.first = item; + processed = true; + } + else if (m_selected_features.second.is_center && m_selected_features.second.source == item.feature) { + // switch 2nd selection from center to its feature + m_selected_features.second = item; + processed = true; + } + } + + if (!processed) { + remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); + if (m_selected_features.second == item) + // 2nd feature deselection + m_selected_features.second.reset(); + else { + // 2nd feature selection + m_selected_features.second = item; + if (requires_sphere_raycaster_for_picking(item)) + m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_2_ID, *m_sphere.mesh_raycaster)); + } + } + } + else { + remove_selected_sphere_raycaster(SEL_SPHERE_1_ID); + if (m_selected_features.second.feature.has_value()) { + // promote 2nd feature to 1st feature + remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); + m_selected_features.first = m_selected_features.second; + if (requires_sphere_raycaster_for_picking(m_selected_features.first)) + m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster)); + m_selected_features.second.reset(); + } + else + // 1st feature deselection + m_selected_features.first.reset(); + } + } + else { + // 1st feature selection + const SelectedFeatures::Item item = detect_current_item(); + m_selected_features.first = item; + if (requires_sphere_raycaster_for_picking(item)) + m_selected_sphere_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SEL_SPHERE_1_ID, *m_sphere.mesh_raycaster)); + } + + update_measurement_result(); + + m_imgui->set_requires_extra_frame(); + + return true; + } + else + // if the mouse pointer is on any volume, filter out the event to prevent the user to move it + // equivalent tp: return (m_parent.get_first_hover_volume_idx() != -1); + return m_curr_feature.has_value(); + + // fix: prevent restart gizmo when reselect object + // take responsibility for left up + if (m_parent.get_first_hover_volume_idx() >= 0) + m_mouse_left_down = true; + } + else if (mouse_event.LeftUp()) { + if (m_mouse_left_down) { + // responsible for mouse left up after selecting plane + m_mouse_left_down = false; + return true; + } + if (m_hover_id == -1 && !m_parent.is_mouse_dragging()) + // avoid closing the gizmo if the user clicks outside of any volume + return true; + } + else if (mouse_event.RightDown()) { + // let the event pass through to allow panning/rotating the 3D scene + if (mouse_event.CmdDown()) + return false; + } + else if (mouse_event.Leaving()) + m_mouse_left_down = false; + + return false; +} + +void GLGizmoMeasure::data_changed(bool is_serializing) +{ + m_parent.toggle_sla_auxiliaries_visibility(false, nullptr, -1); + + update_if_needed(); + + m_last_inv_zoom = 0.0f; + m_last_plane_idx = -1; + if (m_pending_scale) { + update_measurement_result(); + m_pending_scale = false; + } + else + m_selected_features.reset(); + m_selected_sphere_raycasters.clear(); + m_editing_distance = false; + m_is_editing_distance_first_frame = true; +} + +bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (action == SLAGizmoEventType::ShiftDown) { + if (m_shift_kar_filter.is_first()) { + m_mode = EMode::PointSelection; + disable_scene_raycasters(); + } + m_shift_kar_filter.increase_count(); + } + else if (action == SLAGizmoEventType::ShiftUp) { + m_shift_kar_filter.reset_count(); + m_mode = EMode::FeatureSelection; + restore_scene_raycasters_state(); + } + else if (action == SLAGizmoEventType::Delete) { + m_selected_features.reset(); + m_selected_sphere_raycasters.clear(); + m_parent.request_extra_frame(); + } + else if (action == SLAGizmoEventType::Escape) { + if (!m_selected_features.first.feature.has_value()) { + update_measurement_result(); + return false; + } + else { + if (m_selected_features.second.feature.has_value()) { + remove_selected_sphere_raycaster(SEL_SPHERE_2_ID); + m_selected_features.second.feature.reset(); + } + else { + remove_selected_sphere_raycaster(SEL_SPHERE_1_ID); + m_selected_features.first.feature.reset(); + } + + update_measurement_result(); + } + } + + return true; +} + +bool GLGizmoMeasure::on_init() +{ + m_shortcut_key = WXK_CONTROL_U; + + m_desc["feature_selection_caption"] = _L("ShiftLeft mouse button"); + m_desc["feature_selection"] = _L("Select feature"); + m_desc["point_selection_caption"] = _L("Shift + Left mouse button"); + m_desc["point_selection"] = _L("Select point"); + m_desc["reset_caption"] = _L("Delete"); + m_desc["reset"] = _L("Restart selection"); + m_desc["unselect_caption"] = _L("Esc"); + m_desc["unselect"] = _L("Unselect"); + + return true; +} + +void GLGizmoMeasure::on_set_state() +{ + if (m_state == Off) { + m_parent.toggle_sla_auxiliaries_visibility(true, nullptr, -1); + m_shift_kar_filter.reset_count(); + m_curr_feature.reset(); + m_curr_point_on_feature_position.reset(); + restore_scene_raycasters_state(); + m_editing_distance = false; + m_is_editing_distance_first_frame = true; + m_measuring.reset(); + m_raycaster.reset(); + } + else { + m_mode = EMode::FeatureSelection; + // store current state of scene raycaster for later use + m_scene_raycasters.clear(); + auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (scene_raycasters != nullptr) { + m_scene_raycasters.reserve(scene_raycasters->size()); + for (auto r : *scene_raycasters) { + SceneRaycasterState state = { r, r->is_active() }; + m_scene_raycasters.emplace_back(state); + } + } + } +} + +std::string GLGizmoMeasure::on_get_name() const +{ + return _u8L("Measure"); +} + +bool GLGizmoMeasure::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + selection.is_single_full_instance() : + selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); + if (res) + res &= !selection.contains_sinking_volumes(); + + return res; +} + +void GLGizmoMeasure::on_render() +{ +#if ENABLE_MEASURE_GIZMO_DEBUG + render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + +// // do not render if the user is panning/rotating the 3d scene +// if (m_parent.is_mouse_dragging()) +// return; + + update_if_needed(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + const float inv_zoom = (float)camera.get_inv_zoom(); + + Vec3f position_on_model; + Vec3f normal_on_model; + size_t model_facet_idx; + const bool mouse_on_object = m_raycaster->unproject_on_mesh(m_mouse_pos, Transform3d::Identity(), camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); + const bool is_hovering_on_feature = m_mode == EMode::PointSelection && m_hover_id != -1; + + auto update_circle = [this, inv_zoom]() { + if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { + m_last_inv_zoom = inv_zoom; + m_last_circle = m_curr_feature; + m_circle.reset(); + const auto [center, radius, normal] = m_curr_feature->get_circle(); + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), Transform3f::Identity()); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); + m_circle.model.init_from(std::move(circle_geometry)); + return true; + } + return false; + }; + + if (m_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) { + if (m_hover_id == SEL_SPHERE_1_ID || m_hover_id == SEL_SPHERE_2_ID) { + // Skip feature detection if hovering on a selected point/center + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_curr_feature.reset(); + m_curr_point_on_feature_position.reset(); + } + else { + std::optional curr_feature = wxGetMouseState().LeftIsDown() ? m_curr_feature : + mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; + + if (m_curr_feature != curr_feature || + (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_raycasters.clear(); + m_curr_feature = curr_feature; + if (!m_curr_feature.has_value()) + return; + + switch (m_curr_feature->get_type()) { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + update_circle(); + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto [idx, normal, point] = m_curr_feature->get_plane(); + if (m_last_plane_idx != idx) { + m_last_plane_idx = idx; + const indexed_triangle_set& its = m_measuring->get_its(); + const std::vector& plane_triangles = m_measuring->get_plane_triangle_indices(idx); + GLModel::Geometry init_data = init_plane_data(its, plane_triangles); + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); + } + + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } + } + } + } + + if (m_mode != EMode::PointSelection) + m_curr_point_on_feature_position.reset(); + else if (is_hovering_on_feature) { + auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { + auto it = m_raycasters.find(feature_type_id); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); + if (res) { + if (callback) + p = callback(p); + return trafo * p.cast(); + } + } + return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); + }; + + if (m_curr_feature.has_value()) { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + m_curr_point_on_feature_position = m_curr_feature->get_point(); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value() && m_hover_id == POINT_ID) + m_curr_point_on_feature_position = *extra; + else { + const Vec3d pos = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + if (!pos.isApprox(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX))) + m_curr_point_on_feature_position = pos; + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto [center, radius, normal] = m_curr_feature->get_circle(); + if (m_hover_id == POINT_ID) + m_curr_point_on_feature_position = center; + else { + const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); + const Eigen::Hyperplane plane(normal, center); + const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Vec3d local_proj = local_to_model_matrix.inverse() * plane.projection(world_pof); + double angle = std::atan2(local_proj.y(), local_proj.x()); + if (angle < 0.0) + angle += 2.0 * double(M_PI); + + const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0); + m_curr_point_on_feature_position = local_to_model_matrix * local_pos; + } + break; + } + } + } + } + else { + m_curr_point_on_feature_position.reset(); + if (m_curr_feature.has_value() && m_curr_feature->get_type() == Measure::SurfaceFeatureType::Circle) { + if (update_circle()) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end()) + m_raycasters.erase(it); + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + } + } + } + + if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + const bool old_cullface = ::glIsEnabled(GL_CULL_FACE); + glsafe(::glDisable(GL_CULL_FACE)); + + const Transform3d& view_matrix = camera.get_view_matrix(); + + auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + }; + + auto set_emission_uniform = [shader](const ColorRGBA& color, bool hover) { + shader->set_uniform("emission_factor", /*(color == GLVolume::SELECTED_COLOR) ? 0.0f :*/ + hover ? 0.5f : 0.25f); + }; + + auto render_feature = [this, set_matrix_uniforms, set_emission_uniform](const Measure::SurfaceFeature& feature, const std::vector& colors, + float inv_zoom, bool hover, bool update_raycasters_transform) { + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Transform3d feature_matrix = Geometry::translation_transform(feature.get_point()) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); + set_emission_uniform(colors.front(), hover); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters_transform) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = feature.get_circle(); + // render circle + const Transform3d circle_matrix = Transform3d::Identity(); + set_matrix_uniforms(circle_matrix); + if (update_raycasters_transform) { + set_emission_uniform(colors.front(), hover); + m_circle.model.set_color(colors.front()); + m_circle.model.render(); + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + else { + GLModel circle; + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), Transform3f::Identity()); + circle.init_from(std::move(circle_geometry)); + set_emission_uniform(colors.front(), hover); + circle.set_color(colors.front()); + circle.render(); + } + // render center + if (colors.size() > 1) { + const Transform3d center_matrix = Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + set_emission_uniform(colors.back(), hover); + m_sphere.model.set_color(colors.back()); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [from, to] = feature.get_edge(); + // render edge + const Transform3d edge_matrix = Geometry::translation_transform(from) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), to - from) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to - from).norm() }); + set_matrix_uniforms(edge_matrix); + set_emission_uniform(colors.front(), hover); + m_cylinder.model.set_color(colors.front()); + m_cylinder.model.render(); + if (update_raycasters_transform) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(edge_matrix); + } + + // render extra point + if (colors.size() > 1) { + const std::optional extra = feature.get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + set_emission_uniform(colors.back(), hover); + m_sphere.model.set_color(colors.back()); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(Transform3d::Identity()); + set_emission_uniform(colors.front(), hover); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters_transform) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(Transform3d::Identity()); + } + break; + } + } + }; + + auto hover_selection_color = [this]() { + return ((m_mode == EMode::PointSelection && !m_selected_features.first.feature.has_value()) || + (m_mode != EMode::PointSelection && (!m_selected_features.first.feature.has_value() || *m_curr_feature == *m_selected_features.first.feature))) ? + SELECTED_1ST_COLOR : SELECTED_2ND_COLOR; + }; + + auto hovering_color = [this, hover_selection_color]() { + return (m_mode == EMode::PointSelection) ? HOVER_COLOR : hover_selection_color(); + }; + + if (m_curr_feature.has_value()) { + // render hovered feature + + std::vector colors; + if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) { + // hovering over the 1st selected feature + if (m_selected_features.first.is_center) + // hovering over a center + colors = { NEUTRAL_COLOR, hovering_color() }; + else if (is_feature_with_center(*m_selected_features.first.feature)) + // hovering over a feature with center + colors = { hovering_color(), NEUTRAL_COLOR }; + else + colors = { hovering_color() }; + } + else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature) { + // hovering over the 2nd selected feature + if (m_selected_features.second.is_center) + // hovering over a center + colors = { NEUTRAL_COLOR, hovering_color() }; + else if (is_feature_with_center(*m_selected_features.second.feature)) + // hovering over a feature with center + colors = { hovering_color(), NEUTRAL_COLOR }; + else + colors = { hovering_color() }; + } + else { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + colors.emplace_back(hover_selection_color()); + break; + } + case Measure::SurfaceFeatureType::Edge: + case Measure::SurfaceFeatureType::Circle: + { + if (m_selected_features.first.is_center && m_curr_feature == m_selected_features.first.source) + colors = { SELECTED_1ST_COLOR, NEUTRAL_COLOR }; + else if (m_selected_features.second.is_center && m_curr_feature == m_selected_features.second.source) + colors = { SELECTED_2ND_COLOR, NEUTRAL_COLOR }; + else + colors = { hovering_color(), hovering_color() }; + break; + } + case Measure::SurfaceFeatureType::Plane: + { + colors.emplace_back(hovering_color()); + break; + } + } + } + + render_feature(*m_curr_feature, colors, inv_zoom, true, true); + } + + if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { + // render 1st selected feature + + std::optional feature_to_render; + std::vector colors; + bool requires_raycaster_update = false; + if (m_hover_id == SEL_SPHERE_1_ID && (m_selected_features.first.is_center || is_feature_with_center(*m_selected_features.first.feature))) { + // hovering over a center + feature_to_render = m_selected_features.first.source; + colors = { NEUTRAL_COLOR, SELECTED_1ST_COLOR }; + requires_raycaster_update = true; + } + else if (is_feature_with_center(*m_selected_features.first.feature)) { + // hovering over a feature with center + feature_to_render = m_selected_features.first.feature; + colors = { SELECTED_1ST_COLOR, NEUTRAL_COLOR }; + requires_raycaster_update = true; + } + else { + feature_to_render = m_selected_features.first.feature; + colors = { SELECTED_1ST_COLOR }; + requires_raycaster_update = m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point; + } + + render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_1_ID, false); + + if (requires_raycaster_update) { + auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SEL_SPHERE_1_ID; }); + if (it != m_selected_sphere_raycasters.end()) + (*it)->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.first.feature)) * Geometry::scale_transform(inv_zoom)); + } + } + + if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { + // render 2nd selected feature + + std::optional feature_to_render; + std::vector colors; + bool requires_raycaster_update = false; + if (m_hover_id == SEL_SPHERE_2_ID && (m_selected_features.second.is_center || is_feature_with_center(*m_selected_features.second.feature))) { + // hovering over a center + feature_to_render = m_selected_features.second.source; + colors = { NEUTRAL_COLOR, SELECTED_2ND_COLOR }; + requires_raycaster_update = true; + } + else if (is_feature_with_center(*m_selected_features.second.feature)) { + // hovering over a feature with center + feature_to_render = m_selected_features.second.feature; + colors = { SELECTED_2ND_COLOR, NEUTRAL_COLOR }; + requires_raycaster_update = true; + } + else { + feature_to_render = m_selected_features.second.feature; + colors = { SELECTED_2ND_COLOR }; + requires_raycaster_update = m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point; + } + + render_feature(*feature_to_render, colors, inv_zoom, m_hover_id == SEL_SPHERE_2_ID, false); + + if (requires_raycaster_update) { + auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SEL_SPHERE_2_ID; }); + if (it != m_selected_sphere_raycasters.end()) + (*it)->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.second.feature)) * Geometry::scale_transform(inv_zoom)); + } + } + + if (is_hovering_on_feature && m_curr_point_on_feature_position.has_value()) { + if (m_hover_id != POINT_ID) { + // render point on feature while SHIFT is pressed + const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(matrix); + const ColorRGBA color = hover_selection_color(); + set_emission_uniform(color, true); + m_sphere.model.set_color(color); + m_sphere.model.render(); + } + } + + shader->stop_using(); + + if (old_cullface) + glsafe(::glEnable(GL_CULL_FACE)); + + render_dimensioning(); +} + +void GLGizmoMeasure::update_if_needed() +{ + auto update_plane_models_cache = [this](const indexed_triangle_set& its) { + m_plane_models_cache.clear(); + m_plane_models_cache.resize(m_measuring->get_num_of_planes(), GLModel()); + + auto& plane_models_cache = m_plane_models_cache; + const auto& measuring = m_measuring; + + //for (int idx = 0; idx < m_measuring->get_num_of_planes(); ++idx) { + tbb::parallel_for(tbb::blocked_range(0, m_measuring->get_num_of_planes()), + [&plane_models_cache, &measuring, &its](const tbb::blocked_range& range) { + for (size_t idx = range.begin(); idx != range.end(); ++idx) { + GLModel::Geometry init_data = init_plane_data(its, measuring->get_plane_triangle_indices(idx)); + plane_models_cache[idx].init_from(std::move(init_data)); + } + }); + }; + + auto do_update = [this, update_plane_models_cache](const std::vector& volumes_cache, const Selection& selection) { + TriangleMesh composite_mesh; + for (const auto& vol : volumes_cache) { +// if (selection.is_single_full_instance() && vol.volume->is_modifier()) +// continue; + + TriangleMesh volume_mesh = vol.volume->mesh(); + volume_mesh.transform(vol.world_trafo); + + if (vol.world_trafo.matrix().determinant() < 0.0) + volume_mesh.flip_triangles(); + + composite_mesh.merge(volume_mesh); + } + + m_measuring.reset(new Measure::Measuring(composite_mesh.its)); + update_plane_models_cache(m_measuring->get_its()); + m_raycaster.reset(new MeshRaycaster(std::make_shared(composite_mesh))); + m_volumes_cache = volumes_cache; + }; + + const Selection& selection = m_parent.get_selection(); + if (selection.is_empty()) + return; + + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + std::vector volumes_cache; + volumes_cache.reserve(idxs.size()); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + const int volume_idx = v->volume_idx(); + if (volume_idx < 0) + continue; + + const ModelObject* obj = selection.get_model()->objects[v->object_idx()]; + const ModelInstance* inst = obj->instances[v->instance_idx()]; + const ModelVolume* vol = obj->volumes[volume_idx]; + const VolumeCacheItem item = { + obj, inst, vol, + Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst->get_matrix() * vol->get_matrix() + }; + volumes_cache.emplace_back(item); + } + + if (m_state != On || volumes_cache.empty()) + return; + + if (m_measuring == nullptr || m_volumes_cache != volumes_cache) + do_update(volumes_cache, selection); +} + +void GLGizmoMeasure::disable_scene_raycasters() +{ + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(false); + } +} + +void GLGizmoMeasure::restore_scene_raycasters_state() +{ + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(r.state); + } +} + +void GLGizmoMeasure::render_dimensioning() +{ + static SelectedFeatures last_selected_features; + + if (!m_selected_features.first.feature.has_value()) + return; + + if (!m_selected_features.second.feature.has_value() && m_selected_features.first.feature->get_type() != Measure::SurfaceFeatureType::Circle) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + auto point_point = [this, &shader](const Vec3d& v1, const Vec3d& v2, float distance) { + if ((v2 - v1).squaredNorm() < 0.000001 || distance < 0.001f) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + + // screen coordinates + const Vec2d v1ss = TransformHelper::world_to_ss(v1, projection_view_matrix, viewport); + const Vec2d v2ss = TransformHelper::world_to_ss(v2, projection_view_matrix, viewport); + + if (v1ss.isApprox(v2ss)) + return; + + const Vec2d v12ss = v2ss - v1ss; + const double v12ss_len = v12ss.norm(); + + const bool overlap = v12ss_len - 2.0 * TRIANGLE_HEIGHT < 0.0; + + const auto q12ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(v12ss.x(), v12ss.y(), 0.0)); + const auto q21ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(-v12ss.x(), -v12ss.y(), 0.0)); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; + const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + + const Transform3d ss_to_ndc_matrix = TransformHelper::ndc_to_ss_matrix_inverse(viewport); + +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("dashed_thick_lines"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + const std::array& viewport = camera.get_viewport(); + shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3]))); + shader->set_uniform("width", 1.0f); + shader->set_uniform("gap_size", 0.0f); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.0f)); + + // stem + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::WHITE()); + m_dimensioning.line.render(); + +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(1.0f)); + + // arrow 1 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); + m_dimensioning.triangle.render(); + + // arrow 2 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); + m_dimensioning.triangle.render(); + + const bool use_inches = wxGetApp().app_config->get_bool("use_inches"); + const double curr_value = use_inches ? GizmoObjectManipulation::mm_to_in * distance : distance; + const std::string curr_value_str = format_double(curr_value); + const std::string units = use_inches ? _u8L("in") : _u8L("mm"); + const float value_str_width = 20.0f + ImGui::CalcTextSize(curr_value_str.c_str()).x; + static double edit_value = 0.0; + + ImGuiWrapper::push_common_window_style(m_parent.get_scale()); + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + + if (!m_editing_distance) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + ImGui::AlignTextToFramePadding(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const std::string txt = curr_value_str + " " + units; + ImVec2 txt_size = ImGui::CalcTextSize(txt.c_str()); + const ImGuiStyle& style = ImGui::GetStyle(); + draw_list->AddRectFilled({ pos.x - style.FramePadding.x, pos.y + style.FramePadding.y }, { pos.x + txt_size.x + 2.0f * style.FramePadding.x , pos.y + txt_size.y + 2.0f * style.FramePadding.y }, + ImGuiWrapper::to_ImU32(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f))); + ImGui::SetCursorScreenPos({ pos.x + style.FramePadding.x, pos.y }); + m_imgui->text(txt); + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = curr_value; + m_imgui->requires_extra_frame(); + } + m_imgui->end(); + ImGui::PopStyleVar(3); + } + + if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) + ImGui::OpenPopup("distance_popup"); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); + if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + auto perform_scale = [this](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_u8L("Scale")); + + struct TrafoData + { + double ratio; + Vec3d old_pivot; + Vec3d new_pivot; + Transform3d scale_matrix; + + TrafoData(double ratio, const Vec3d& old_pivot, const Vec3d& new_pivot) { + this->ratio = ratio; + this->scale_matrix = Geometry::scale_transform(ratio); + this->old_pivot = old_pivot; + this->new_pivot = new_pivot; + } + + Vec3d transform(const Vec3d& point) const { return this->scale_matrix * (point - this->old_pivot) + this->new_pivot; } + }; + + auto scale_feature = [](Measure::SurfaceFeature& feature, const TrafoData& trafo_data) { + switch (feature.get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + feature = Measure::SurfaceFeature(trafo_data.transform(feature.get_point())); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto [from, to] = feature.get_edge(); + const std::optional extra = feature.get_extra_point(); + const std::optional new_extra = extra.has_value() ? trafo_data.transform(*extra) : extra; + feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, trafo_data.transform(from), trafo_data.transform(to), new_extra); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto [center, radius, normal] = feature.get_circle(); + feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Circle, trafo_data.transform(center), normal, std::nullopt, trafo_data.ratio * radius); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto [idx, normal, origin] = feature.get_plane(); + feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Plane, normal, trafo_data.transform(origin), std::nullopt, idx); + break; + } + default: { break; } + } + }; + + // apply scale + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // scale selection + Selection& selection = m_parent.get_selection(); + const Vec3d old_center = selection.get_bounding_box().center(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + + // scale dimensioning + const Vec3d new_center = selection.get_bounding_box().center(); + const TrafoData trafo_data(ratio, old_center, new_center); + scale_feature(*m_selected_features.first.feature, trafo_data); + if (m_selected_features.second.feature.has_value()) + scale_feature(*m_selected_features.second.feature, trafo_data); + + // update measure on next call to data_changed() + m_pending_scale = true; + }; + auto action_exit = [this]() { + m_editing_distance = false; + m_is_editing_distance_first_frame = true; + ImGui::CloseCurrentPopup(); + }; + auto action_scale = [perform_scale, action_exit](double new_value, double old_value) { + perform_scale(new_value, old_value); + action_exit(); + }; + + m_imgui->disable_background_fadeout_animation(); + ImGui::PushItemWidth(value_str_width); + if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { + } + + // trick to auto-select text in the input widgets on 1st frame + if (m_is_editing_distance_first_frame) { + ImGui::SetKeyboardFocusHere(0); + m_is_editing_distance_first_frame = false; + m_imgui->set_requires_extra_frame(); + } + + // handle keys input + if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + action_scale(edit_value, curr_value); + else if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + action_exit(); + + ImGui::SameLine(); + ImGuiWrapper::push_confirm_button_style(); + if (m_imgui->button(_CTX(L_CONTEXT("Scale", "Verb"), "Verb"))) + action_scale(edit_value, curr_value); + ImGuiWrapper::pop_confirm_button_style(); + ImGui::SameLine(); + ImGuiWrapper::push_cancel_button_style(); + if (m_imgui->button(_L("Cancel"))) + action_exit(); + ImGuiWrapper::pop_cancel_button_style(); + ImGui::EndPopup(); + } + ImGui::PopStyleVar(4); + ImGuiWrapper::pop_common_window_style(); + }; + + auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Point && f2.get_type() == Measure::SurfaceFeatureType::Edge); + std::pair e = f2.get_edge(); + const Vec3d v_proj = m_measurement_result.distance_infinite->to; + const Vec3d e1e2 = e.second - e.first; + const Vec3d v_proje1 = v_proj - e.first; + const bool on_e1_side = v_proje1.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && v_proje1.norm() > e1e2.norm(); + if (on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + const Transform3d ss_to_ndc_matrix = TransformHelper::ndc_to_ss_matrix_inverse(viewport); + + const Vec2d v_projss = TransformHelper::world_to_ss(v_proj, projection_view_matrix, viewport); + auto render_extension = [this, &v_projss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d pss = TransformHelper::world_to_ss(p, projection_view_matrix, viewport); + if (!pss.isApprox(v_projss)) { + const Vec2d pv_projss = v_projss - pss; + const double pv_projss_len = pv_projss.norm(); + + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_projss.x(), pv_projss.y(), 0.0)); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * + Geometry::scale_transform({ pv_projss_len, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); + m_dimensioning.line.render(); + } + }; + + render_extension(on_e1_side ? e.first : e.second); + } + }; + + auto arc_edge_edge = [this, &shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); + if (!m_measurement_result.angle.has_value()) + return; + + const double angle = m_measurement_result.angle->angle; + const Vec3d center = m_measurement_result.angle->center; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + const bool coplanar = m_measurement_result.angle->coplanar; + + if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) + return; + + const double draw_radius = (radius > 0.0) ? radius : calc_radius; + + const Vec3d e1_unit = Measure::edge_direction(e1); + const Vec3d e2_unit = Measure::edge_direction(e2); + + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + const double step = angle / double(resolution); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); + + if (!m_dimensioning.arc.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(resolution + 1); + init_data.reserve_indices(resolution + 1); + + // vertices + indices + for (unsigned int i = 0; i <= resolution; ++i) { + const double a = step * double(i); + const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + init_data.add_vertex((Vec3f)v.cast()); + init_data.add_index(i); + } + + m_dimensioning.arc.init_from(std::move(init_data)); + } + + const Camera& camera = wxGetApp().plater()->get_camera(); +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("dashed_thick_lines"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + const std::array& viewport = camera.get_viewport(); + shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3]))); + shader->set_uniform("width", 1.0f); + shader->set_uniform("gap_size", 0.0f); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.0f)); + + // arc + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); + +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(1.0f)); + + // arrows + auto render_arrow = [this, shader, &camera, &normal, ¢er, &e1_unit, draw_radius, step, resolution](unsigned int endpoint_id) { + const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); + const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); + const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const auto qz = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), (endpoint_id == 1) ? normal : -normal); + const auto qx = Eigen::Quaternion::FromTwoVectors(qz * Vec3d::UnitX(), direction_model); + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(position_model) * + qx * qz * Geometry::scale_transform(camera.get_inv_zoom()); + shader->set_uniform("view_model_matrix", view_model_matrix); + m_dimensioning.triangle.render(); + }; + + glsafe(::glDisable(GL_CULL_FACE)); + render_arrow(1); + render_arrow(2); + glsafe(::glEnable(GL_CULL_FACE)); + + // edge 1 extension + const Vec3d e11e12 = e1.second - e1.first; + const Vec3d e11center = center - e1.first; + const double e11center_len = e11center.norm(); + if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * + Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); + m_dimensioning.line.render(); + } + + // edge 2 extension + const Vec3d e21center = center - e2.first; + const double e21center_len = e21center.norm(); + if (e21center_len > EPSILON) { + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * + Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); + m_dimensioning.line.render(); + } + + // label + // label world coordinates + const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + + // label screen coordinates + const std::array& viewport = camera.get_viewport(); + const Vec2d label_position_ss = TransformHelper::world_to_ss(label_position_world, + camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); + + ImGuiWrapper::push_common_window_style(m_parent.get_scale()); + m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(wxString("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + ImGui::AlignTextToFramePadding(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; + ImVec2 txt_size = ImGui::CalcTextSize(txt.c_str()); + const ImGuiStyle& style = ImGui::GetStyle(); + draw_list->AddRectFilled({ pos.x - style.FramePadding.x, pos.y + style.FramePadding.y }, { pos.x + txt_size.x + 2.0f * style.FramePadding.x , pos.y + txt_size.y + 2.0f * style.FramePadding.y }, + ImGuiWrapper::to_ImU32(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f))); + ImGui::SetCursorScreenPos({ pos.x + style.FramePadding.x, pos.y }); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); + ImGuiWrapper::pop_common_window_style(); + }; + + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); + if (!m_measurement_result.angle.has_value()) + return; + + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + + if (calc_radius == 0.0) + return; + + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); + }; + + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); + if (!m_measurement_result.angle.has_value()) + return; + + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + + if (calc_radius == 0.0) + return; + + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); + }; + + shader->start_using(); + + if (!m_dimensioning.line.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(1.0f, 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_dimensioning.line.init_from(std::move(init_data)); + } + + if (!m_dimensioning.triangle.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(3); + init_data.reserve_indices(3); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, 0.5f * TRIANGLE_BASE, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, -0.5f * TRIANGLE_BASE, 0.0f)); + + // indices + init_data.add_triangle(0, 1, 2); + + m_dimensioning.triangle.init_from(std::move(init_data)); + } + + if (last_selected_features != m_selected_features) + m_dimensioning.arc.reset(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + + const bool has_distance = m_measurement_result.has_distance_data(); + + const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); + const Measure::SurfaceFeature* f2 = nullptr; + std::unique_ptr temp_feature; + if (m_selected_features.second.feature.has_value()) + f2 = &(*m_selected_features.second.feature); + else { + assert(m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle); + temp_feature = std::make_unique(std::get<0>(m_selected_features.first.feature->get_circle())); + f2 = temp_feature.get(); + } + + if (!m_selected_features.second.feature.has_value() && m_selected_features.first.feature->get_type() != Measure::SurfaceFeatureType::Circle) + return; + + Measure::SurfaceFeatureType ft1 = f1->get_type(); + Measure::SurfaceFeatureType ft2 = f2->get_type(); + + // Order features by type so following conditions are simple. + if (ft1 > ft2) { + std::swap(ft1, ft2); + std::swap(f1, f2); + } + + // If there is an angle to show, draw the arc: + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) + arc_edge_edge(*f1, *f2); + else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + arc_edge_plane(*f1, *f2); + else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane) + arc_plane_plane(*f1, *f2); + + if (has_distance){ + // Where needed, draw the extension of the edge to where the dist is measured: + if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + point_edge(*f1, *f2); + + // Render the arrow between the points that the backend passed: + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to, dap.dist); + } + + glsafe(::glEnable(GL_DEPTH_TEST)); + + shader->stop_using(); +} + +static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) +{ + assert(col_1 != nullptr && col_2 != nullptr); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + col_1(); + ImGui::TableSetColumnIndex(1); + col_2(); +} + +static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) +{ + add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); +}; + +#if ENABLE_MEASURE_GIZMO_DEBUG +void GLGizmoMeasure::render_debug_dialog() +{ + auto add_feature_data = [this](const SelectedFeatures::Item& item) { + const std::string text = (item.source == item.feature) ? surface_feature_type_as_string(item.feature->get_type()) : point_on_feature_type_as_string(item.source->get_type(), m_hover_id); + add_strings_row_to_table(*m_imgui, "Type", ImGuiWrapper::COL_ORCA, text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + switch (item.feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORCA, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = item.feature->get_edge(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORCA, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORCA, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = item.feature->get_plane(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORCA, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORCA, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORCA, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = item.feature->get_circle(); + const Vec3d on_circle = center + radius * Measure::get_orthogonal(normal, true); + radius = (on_circle - center).norm(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORCA, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORCA, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORCA, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + std::optional extra_point = item.feature->get_extra_point(); + if (extra_point.has_value()) + add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORCA, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + }; + + m_imgui->begin("Measure tool debug", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (ImGui::BeginTable("Mode", 2)) { + std::string txt; + switch (m_mode) + { + case EMode::FeatureSelection: { txt = "Feature selection"; break; } + case EMode::PointSelection: { txt = "Point selection"; break; } + default: { assert(false); break; } + } + add_strings_row_to_table(*m_imgui, "Mode", ImGuiWrapper::COL_ORCA, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + + ImGui::Separator(); + if (ImGui::BeginTable("Hover", 2)) { + add_strings_row_to_table(*m_imgui, "Hover id", ImGuiWrapper::COL_ORCA, std::to_string(m_hover_id), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + const std::string txt = m_curr_feature.has_value() ? surface_feature_type_as_string(m_curr_feature->get_type()) : "None"; + add_strings_row_to_table(*m_imgui, "Current feature", ImGuiWrapper::COL_ORCA, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); + } + + ImGui::Separator(); + if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) + m_imgui->text("Empty selection"); + else { + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; + if (m_selected_features.first.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORCA, "Selection 1"); + if (ImGui::BeginTable("Selection 1", 2, flags)) { + add_feature_data(m_selected_features.first); + ImGui::EndTable(); + } + } + if (m_selected_features.second.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORCA, "Selection 2"); + if (ImGui::BeginTable("Selection 2", 2, flags)) { + add_feature_data(m_selected_features.second); + ImGui::EndTable(); + } + } + } + m_imgui->end(); +} +#endif // ENABLE_MEASURE_GIZMO_DEBUG + +void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +{ + static std::optional last_feature; + static EMode last_mode = EMode::FeatureSelection; + static SelectedFeatures last_selected_features; + + static float last_y = 0.0f; + static float last_h = 0.0f; + + if (m_editing_distance) + return; + + // adjust window position to avoid overlap the view toolbar + const float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); + if (last_h != win_h || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + + // Orca + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + float caption_max = 0.f; + float total_text_max = 0.f; + for (const auto &t : std::array{"feature_selection", "point_selection", "reset", "unselect"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); + total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); + } + + const bool use_inches = wxGetApp().app_config->get_bool("use_inches"); + const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); + + // Show selection + { + auto format_item_text = [this, use_inches, &units](const SelectedFeatures::Item& item) { + if (!item.feature.has_value()) + return _u8L("None"); + + std::string text = (item.source == item.feature) ? surface_feature_type_as_string(item.feature->get_type()) : + item.is_center ? center_on_feature_type_as_string(item.source->get_type()) : point_on_feature_type_as_string(item.source->get_type(), m_hover_id); + if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + auto [center, radius, normal] = item.feature->get_circle(); + const Vec3d on_circle = center + radius * Measure::get_orthogonal(normal, true); + radius = (on_circle - center).norm(); + if (use_inches) + radius = GizmoObjectManipulation::mm_to_in * radius; + text += " (" + _u8L("Diameter") + ": " + format_double(2.0 * radius) + units + ")"; + } + else if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + auto [start, end] = item.feature->get_edge(); + double length = (end - start).norm(); + if (use_inches) + length = GizmoObjectManipulation::mm_to_in * length; + text += " (" + _u8L("Length") + ": " + format_double(length) + units + ")"; + } + return text; + }; + + const float selection_cap_length = ImGui::CalcTextSize((_u8L("Selection") + " 1").c_str()).x * 2; + + ImGui::AlignTextToFramePadding(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + m_imgui->text(_u8L("Selection") + " 1"); + ImGui::SameLine(selection_cap_length); + m_imgui->text(format_item_text(m_selected_features.first)); + ImGui::PopStyleColor(); + + ImGui::AlignTextToFramePadding(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + m_imgui->text(_u8L("Selection") + " 2"); + ImGui::SameLine(selection_cap_length); + m_imgui->text(format_item_text(m_selected_features.second)); + ImGui::PopStyleColor(); + } + + m_imgui->disabled_begin(!m_selected_features.first.feature.has_value()); + if (m_imgui->button(_L("Restart selection"))) { + m_selected_features.reset(); + m_selected_sphere_raycasters.clear(); + m_imgui->set_requires_extra_frame(); + } + m_imgui->disabled_end(); + + auto add_measure_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(col_1_color, col_1); + ImGui::TableSetColumnIndex(1); + m_imgui->text_colored(col_2_color, col_2); + ImGui::TableSetColumnIndex(2); + if (m_imgui->image_button(m_is_dark_mode ? ImGui::ClipboardBtnDarkIcon : ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { + wxTheClipboard->Open(); + wxTheClipboard->SetData(new wxTextDataObject(wxString((col_1 + ": " + col_2).c_str(), wxConvUTF8))); + wxTheClipboard->Close(); + } + }; + + ImGui::Separator(); + m_imgui->text(_u8L("Measure")); + + const unsigned int max_measure_row_count = 2; + unsigned int measure_row_count = 0; + if (ImGui::BeginTable("Measure", 4)) { + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult& measure = m_measurement_result; + if (measure.angle.has_value()) { + ImGui::PushID("ClipboardAngle"); + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORCA, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + + const bool show_strict = measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON); + + if (measure.distance_infinite.has_value()) { + double distance = measure.distance_infinite->dist; + if (use_inches) + distance = GizmoObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceInfinite"); + add_measure_row_to_table(show_strict ? _u8L("Perpendicular distance") : _u8L("Distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (show_strict) { + double distance = measure.distance_strict->dist; + if (use_inches) + distance = GizmoObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceStrict"); + add_measure_row_to_table(_u8L("Direct distance"), ImGuiWrapper::COL_ORCA, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + Vec3d distance = *measure.distance_xyz; + if (use_inches) + distance = GizmoObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceXYZ"); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORCA, format_vec3(distance), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORCA, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + show_tooltip_information(caption_max, x, get_cur_y); + + float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::PopStyleVar(2); + + if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { + // the dialog may have changed its size, ask for an extra frame to render it properly + last_feature = m_curr_feature; + last_mode = m_mode; + last_selected_features = m_selected_features; + m_imgui->set_requires_extra_frame(); + } + + GizmoImguiEnd(); + + // Orca + ImGuiWrapper::pop_toolbar_style(); +} + +void GLGizmoMeasure::on_register_raycasters_for_picking() +{ + // the features are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoMeasure::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); + m_raycasters.clear(); + m_selected_sphere_raycasters.clear(); +} + +void GLGizmoMeasure::remove_selected_sphere_raycaster(int id) +{ + auto it = std::find_if(m_selected_sphere_raycasters.begin(), m_selected_sphere_raycasters.end(), + [id](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == id; }); + if (it != m_selected_sphere_raycasters.end()) + m_selected_sphere_raycasters.erase(it); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, id); +} + +void GLGizmoMeasure::update_measurement_result() +{ + if (!m_selected_features.first.feature.has_value()) + m_measurement_result = Measure::MeasurementResult(); + else if (m_selected_features.second.feature.has_value()) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); + else if (!m_selected_features.second.feature.has_value() && m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, Measure::SurfaceFeature(std::get<0>(m_selected_features.first.feature->get_circle())), m_measuring.get()); +} + +void GLGizmoMeasure::show_tooltip_information(float caption_max, float x, float y) +{ + ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); + ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + + caption_max += m_imgui->calc_text_size(": ").x + 35.f; + + float font_size = ImGui::GetFontSize(); + ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, ImGui::GetStyle().FramePadding.y }); + ImGui::ImageButton3(normal_id, hover_id, button_size); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip2(ImVec2(x, y)); + auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { + m_imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption); + ImGui::SameLine(caption_max); + m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); + }; + + for (const auto &t : std::array{"feature_selection", "point_selection", "reset", "unselect"}) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp new file mode 100644 index 0000000000..0fc7577b43 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -0,0 +1,198 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_GLGizmoMeasure_hpp_ +#define slic3r_GLGizmoMeasure_hpp_ + +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/GUI_Utils.hpp" +#include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "libslic3r/Measure.hpp" +#include "libslic3r/Model.hpp" + +namespace Slic3r { + +enum class ModelVolumeType : int; + +namespace Measure { class Measuring; } + + +namespace GUI { + +enum class SLAGizmoEventType : unsigned char; + +class GLGizmoMeasure : public GLGizmoBase +{ + enum class EMode : unsigned char + { + FeatureSelection, + PointSelection + }; + + struct SelectedFeatures + { + struct Item + { + bool is_center{ false }; + std::optional source; + std::optional feature; + + bool operator == (const Item& other) const { + return this->is_center == other.is_center && this->source == other.source && this->feature == other.feature; + } + + bool operator != (const Item& other) const { + return !operator == (other); + } + + void reset() { + is_center = false; + source.reset(); + feature.reset(); + } + }; + + Item first; + Item second; + + void reset() { + first.reset(); + second.reset(); + } + + bool operator == (const SelectedFeatures & other) const { + if (this->first != other.first) return false; + return this->second == other.second; + } + + bool operator != (const SelectedFeatures & other) const { + return !operator == (other); + } + }; + + struct VolumeCacheItem + { + const ModelObject* object{ nullptr }; + const ModelInstance* instance{ nullptr }; + const ModelVolume* volume{ nullptr }; + Transform3d world_trafo; + + bool operator == (const VolumeCacheItem& other) const { + return this->object == other.object && this->instance == other.instance && this->volume == other.volume && + this->world_trafo.isApprox(other.world_trafo); + } + }; + + std::vector m_volumes_cache; + + EMode m_mode{ EMode::FeatureSelection }; + Measure::MeasurementResult m_measurement_result; + + std::unique_ptr m_measuring; // PIMPL + + PickingModel m_sphere; + PickingModel m_cylinder; + PickingModel m_circle; + PickingModel m_plane; + struct Dimensioning + { + GLModel line; + GLModel triangle; + GLModel arc; + }; + Dimensioning m_dimensioning; + + // Uses a standalone raycaster and not the shared one because of the + // difference in how the mesh is updated + std::unique_ptr m_raycaster; + + std::vector m_plane_models_cache; + std::map> m_raycasters; + // used to keep the raycasters for point/center spheres + std::vector> m_selected_sphere_raycasters; + std::optional m_curr_feature; + std::optional m_curr_point_on_feature_position; + struct SceneRaycasterState + { + std::shared_ptr raycaster{ nullptr }; + bool state{true}; + + }; + std::vector m_scene_raycasters; + + // These hold information to decide whether recalculation is necessary: + float m_last_inv_zoom{ 0.0f }; + std::optional m_last_circle; + int m_last_plane_idx{ -1 }; + + bool m_mouse_left_down{ false }; // for detection left_up of this gizmo + + Vec2d m_mouse_pos{ Vec2d::Zero() }; + + KeyAutoRepeatFilter m_shift_kar_filter; + + SelectedFeatures m_selected_features; + bool m_pending_scale{ false }; + bool m_editing_distance{ false }; + bool m_is_editing_distance_first_frame{ true }; + + void update_if_needed(); + + void disable_scene_raycasters(); + void restore_scene_raycasters_state(); + + void render_dimensioning(); + +#if ENABLE_MEASURE_GIZMO_DEBUG + void render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + +public: + GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed(bool is_serializing) override; + + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + + bool wants_enter_leave_snapshots() const override { return true; } + std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); } + std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); } + +protected: + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_render() override; + void on_set_state() override; + + virtual void on_render_input_window(float x, float y, float bottom_limit) override; + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; + + void remove_selected_sphere_raycaster(int id); + void update_measurement_result(); + + // Orca + void show_tooltip_information(float caption_max, float x, float y); + +private: + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index 20107305b7..fb19ff6459 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -50,7 +50,7 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { // Is this hit the closest to the camera so far? @@ -84,6 +84,27 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return true; } +bool GLGizmoMeshBoolean::on_mouse(const wxMouseEvent &mouse_event) +{ + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + + // when control is down we allow scene pan and rotation even when clicking + // over some object + bool control_down = mouse_event.CmdDown(); + bool grabber_contains_mouse = (get_hover_id() != -1); + if (mouse_event.LeftDown()) { + if ((!control_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + // the gizmo got the event and took some action, there is no need + // to do anything more + return true; + } + + return false; +} + bool GLGizmoMeshBoolean::on_init() { m_shortcut_key = WXK_CONTROL_B; @@ -131,8 +152,8 @@ void GLGizmoMeshBoolean::on_render() } } - float src_color[3] = { 1.0f, 1.0f, 1.0f }; - float tool_color[3] = { 0.0f, 150.0f / 255.0f, 136.0f / 255.0f }; + ColorRGB src_color = { 1.0f, 1.0f, 1.0f }; + ColorRGB tool_color = {0.0f, 150.0f / 255.0f, 136.0f / 255.0f}; m_parent.get_selection().render_bounding_box(src_bb, src_color, m_parent.get_scale()); m_parent.get_selection().render_bounding_box(tool_bb, tool_color, m_parent.get_scale()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp index 42fa97eede..c012a68dca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp @@ -55,14 +55,22 @@ public: m_src.reset(); } - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + + /// + /// Implement when want to process mouse events in gizmo + /// Click, Right click, move, drag, ... + /// + /// Keep information about mouse click + /// Return True when use the information and don't want to + /// propagate it otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; protected: virtual bool on_init() override; virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; virtual void on_render() override; - virtual void on_render_for_picking() override {} virtual void on_set_state() override; virtual CommonGizmosDataID on_get_requirements() const override; virtual void on_render_input_window(float x, float y, float bottom_limit); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 517e880d93..d9a396ba10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -1,3 +1,8 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Bubník @bubnikv +///|/ Copyright (c) 2021 Justin Schuh @jschuh +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" @@ -58,11 +63,11 @@ bool GLGizmoMmuSegmentation::on_is_activable() const } //BBS: use the global one in 3DScene.cpp -/*static std::vector> get_extruders_colors() +/*static std::vector get_extruders_colors() { unsigned char rgb_color[3] = {}; std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - std::vector> colors_out(colors.size()); + std::vector colors_out(colors.size()); for (const std::string &color : colors) { Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); size_t color_idx = &color - &colors.front(); @@ -146,7 +151,7 @@ GLGizmoMmuSegmentation::GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::st { } -void GLGizmoMmuSegmentation::render_painter_gizmo() const +void GLGizmoMmuSegmentation::render_painter_gizmo() { const Selection& selection = m_parent.get_selection(); @@ -162,11 +167,10 @@ void GLGizmoMmuSegmentation::render_painter_gizmo() const glsafe(::glDisable(GL_BLEND)); } -void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection) +void GLGizmoMmuSegmentation::data_changed(bool is_serializing) { - GLGizmoPainterBase::set_painter_gizmo_data(selection); - - if (m_state != On || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF || wxGetApp().filaments_cnt() <= 1) + GLGizmoPainterBase::data_changed(is_serializing); + if (m_state != On || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF || wxGetApp().extruders_edited_cnt() <= 1) return; ModelObject* model_object = m_c->selection_info()->model_object(); @@ -192,7 +196,7 @@ void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection) void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const { ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); - auto *shader = wxGetApp().get_shader("mm_gouraud"); + auto* shader = wxGetApp().get_shader("mm_gouraud"); if (!shader) return; shader->start_using(); @@ -221,18 +225,21 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix(); } - bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; + const bool is_left_handed = trafo_matrix.matrix().determinant() < 0.0; if (is_left_handed) glsafe(::glFrontFace(GL_CW)); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); shader->set_uniform("volume_world_matrix", trafo_matrix); shader->set_uniform("volume_mirrored", is_left_handed); - m_triangle_selectors[mesh_id]->render(m_imgui); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); - glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); } @@ -276,18 +283,14 @@ bool GLGizmoMmuSegmentation::on_key_down_select_tool_type(int keyCode) { return true; } -static void render_extruders_combo(const std::string &label, - const std::vector &extruders, - const std::vector> &extruders_colors, - size_t &selection_idx) +static void render_extruders_combo(const std::string& label, + const std::vector& extruders, + const std::vector& extruders_colors, + size_t& selection_idx) { assert(!extruders_colors.empty()); assert(extruders_colors.size() == extruders_colors.size()); - auto convert_to_imu32 = [](const std::array &color) -> ImU32 { - return IM_COL32(uint8_t(color[0] * 255.f), uint8_t(color[1] * 255.f), uint8_t(color[2] * 255.f), uint8_t(color[3] * 255.f)); - }; - size_t selection_out = selection_idx; // It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox. ImGui::BeginGroup(); @@ -303,7 +306,7 @@ static void render_extruders_combo(const std::string &labe ImGui::SameLine(); ImGuiStyle &style = ImGui::GetStyle(); float height = ImGui::GetTextLineHeight(); - ImGui::GetWindowDrawList()->AddRectFilled(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), convert_to_imu32(extruders_colors[extruder_idx])); + ImGui::GetWindowDrawList()->AddRectFilled(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), ImGuiWrapper::to_ImU32(extruders_colors[extruder_idx])); ImGui::GetWindowDrawList()->AddRect(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), IM_COL32_BLACK); ImGui::SetCursorScreenPos(ImVec2(start_position.x + height + height / 2 + style.FramePadding.x, start_position.y)); @@ -321,7 +324,7 @@ static void render_extruders_combo(const std::string &labe ImVec2 p = ImGui::GetCursorScreenPos(); float height = ImGui::GetTextLineHeight(); - ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + height + height / 2, p.y + height), convert_to_imu32(extruders_colors[selection_idx])); + ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + height + height / 2, p.y + height), ImGuiWrapper::to_ImU32(extruders_colors[selection_idx])); ImGui::GetWindowDrawList()->AddRect(p, ImVec2(p.x + height + height / 2, p.y + height), IM_COL32_BLACK); ImGui::SetCursorScreenPos(ImVec2(p.x + height + height / 2 + style.FramePadding.x, p.y)); @@ -448,8 +451,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott const float item_spacing = m_imgui->scaled(0.8f); size_t n_extruder_colors = std::min((size_t)EnforcerBlockerType::ExtruderMax, m_extruders_colors.size()); for (int extruder_idx = 0; extruder_idx < n_extruder_colors; extruder_idx++) { - const std::array &extruder_color = m_extruders_colors[extruder_idx]; - ImVec4 color_vec(extruder_color[0], extruder_color[1], extruder_color[2], extruder_color[3]); + const ColorRGBA &extruder_color = m_extruders_colors[extruder_idx]; + ImVec4 color_vec = ImGuiWrapper::to_ImVec4(extruder_color); std::string color_label = std::string("##extruder color ") + std::to_string(extruder_idx); std::string item_text = std::to_string(extruder_idx + 1); const ImVec2 label_size = ImGui::CalcTextSize(item_text.c_str(), NULL, true); @@ -486,7 +489,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott if (extruder_idx < 9 && ImGui::IsItemHovered()) m_imgui->tooltip(_L("Shortcut Key ") + std::to_string(extruder_idx + 1), max_tooltip_width); // draw filament id - float gray = 0.299 * extruder_color[0] + 0.587 * extruder_color[1] + 0.114 * extruder_color[2]; + float gray = 0.299 * extruder_color.r() + 0.587 * extruder_color.g() + 0.114 * extruder_color.b(); ImGui::SameLine(button_offset + (button_size.x - label_size.x) / 2.f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {10.0,15.0}); if (gray * 255.f < 80.f) @@ -573,7 +576,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -586,7 +589,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); } + if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); } } else if (m_current_tool == ImGui::TriangleButtonIcon) { m_cursor_type = TriangleSelector::CursorType::POINTER; @@ -599,7 +602,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -612,7 +615,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); } + if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); } } else if (m_current_tool == ImGui::FillButtonIcon) { m_cursor_type = TriangleSelector::CursorType::POINTER; @@ -646,7 +649,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -659,7 +662,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true);} + if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true);} } else if (m_current_tool == ImGui::HeightRangeIcon) { m_tool_type = ToolType::BRUSH; @@ -682,7 +685,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -695,7 +698,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); } + if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); } } else if (m_current_tool == ImGui::GapFillIcon) { m_tool_type = ToolType::GAP_FILL; @@ -811,7 +814,7 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors() continue; int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0; - std::vector> ebt_colors; + std::vector ebt_colors; ebt_colors.push_back(m_extruders_colors[size_t(extruder_idx)]); ebt_colors.insert(ebt_colors.end(), m_extruders_colors.begin(), m_extruders_colors.end()); @@ -833,7 +836,7 @@ void GLGizmoMmuSegmentation::update_triangle_selectors_colors() TriangleSelectorPatch* selector = dynamic_cast(m_triangle_selectors[i].get()); int extruder_idx = m_volumes_extruder_idxs[i]; int extruder_color_idx = std::max(0, extruder_idx - 1); - std::vector> ebt_colors; + std::vector ebt_colors; ebt_colors.push_back(m_extruders_colors[extruder_color_idx]); ebt_colors.insert(ebt_colors.end(), m_extruders_colors.begin(), m_extruders_colors.end()); selector->set_ebt_colors(ebt_colors); @@ -871,7 +874,7 @@ PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const } // BBS -std::array GLGizmoMmuSegmentation::get_cursor_hover_color() const +ColorRGBA GLGizmoMmuSegmentation::get_cursor_hover_color() const { if (m_selected_extruder_idx < m_extruders_colors.size()) return m_extruders_colors[m_selected_extruder_idx]; @@ -910,6 +913,7 @@ void GLMmSegmentationGizmo3DScene::release_geometry() { glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id)); triangle_indices_VBO_id = 0; } + this->clear(); } @@ -917,28 +921,39 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const { assert(triangle_indices_idx < this->triangle_indices_VBO_ids.size()); assert(this->triangle_patches.size() == this->triangle_indices_VBO_ids.size()); + assert(this->vertices_VAO_id != 0); assert(this->vertices_VBO_id != 0); assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0); + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr) + return; + + // the following binding is needed to set the vertex attributes glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float)))); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - - // Render using the Vertex Buffer Objects. - if (this->triangle_indices_sizes[triangle_indices_idx] > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[triangle_indices_idx])); - glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_sizes[triangle_indices_idx]), GL_UNSIGNED_INT, nullptr)); - glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + const GLint position_id = shader->get_attrib_location("v_position"); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0)); + glsafe(::glEnableVertexAttribArray(position_id)); } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + // Render using the Vertex Buffer Objects. + if (this->triangle_indices_VBO_ids[triangle_indices_idx] != 0 && + this->triangle_indices_sizes[triangle_indices_idx] > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[triangle_indices_idx])); + glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_sizes[triangle_indices_idx]), GL_UNSIGNED_INT, nullptr)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } void GLMmSegmentationGizmo3DScene::finalize_vertices() { + assert(this->vertices_VAO_id == 0); assert(this->vertices_VBO_id == 0); if (!this->vertices.empty()) { glsafe(::glGenBuffers(1, &this->vertices_VBO_id)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 6b5cde25c7..d816735c68 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -67,9 +67,9 @@ public: GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); ~GLGizmoMmuSegmentation() override = default; - void render_painter_gizmo() const override; + void render_painter_gizmo() override; - void set_painter_gizmo_data(const Selection& selection) override; + void data_changed(bool is_serializing) override; void render_triangles(const Selection& selection) const override; @@ -87,7 +87,7 @@ public: protected: // BBS - std::array get_cursor_hover_color() const override; + ColorRGBA get_cursor_hover_color() const override; void on_set_state() override; EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType(m_selected_extruder_idx + 1); } @@ -103,11 +103,11 @@ protected: std::string get_gizmo_entering_text() const override { return "Entering color painting"; } std::string get_gizmo_leaving_text() const override { return "Leaving color painting"; } - std::string get_action_snapshot_name() override { return "Color painting editing"; } + std::string get_action_snapshot_name() const override { return "Color painting editing"; } // BBS size_t m_selected_extruder_idx = 0; - std::vector> m_extruders_colors; + std::vector m_extruders_colors; std::vector m_volumes_extruder_idxs; // BBS diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 747d8ec89e..91913914a8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -1,4 +1,7 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoMove.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" @@ -23,16 +26,9 @@ const double GLGizmoMove3D::Offset = 10.0; //BBS: GUI refactor: add obj manipulation GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_displacement(Vec3d::Zero()) - , m_snap_step(1.0) - , m_starting_drag_position(Vec3d::Zero()) - , m_starting_box_center(Vec3d::Zero()) - , m_starting_box_bottom_center(Vec3d::Zero()) //BBS: GUI refactor: add obj manipulation , m_object_manipulation(obj_manipulation) -{ - m_vbo_cone.init_from(its_make_cone(1., 1., 2*PI/36)); -} +{} std::string GLGizmoMove3D::get_tooltip() const { @@ -50,12 +46,24 @@ std::string GLGizmoMove3D::get_tooltip() const return ""; } +bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) { + return use_grabbers(mouse_event); +} + +void GLGizmoMove3D::data_changed(bool is_serializing) { + m_grabbers[2].enabled = !m_parent.get_selection().is_wipe_tower(); +} + bool GLGizmoMove3D::on_init() { for (int i = 0; i < 3; ++i) { m_grabbers.push_back(Grabber()); + m_grabbers.back().extensions = GLGizmoBase::EGrabberExtension::PosZ; } + m_grabbers[0].angles = { 0.0, 0.5 * double(PI), 0.0 }; + m_grabbers[1].angles = { -0.5 * double(PI), 0.0, 0.0 }; + m_shortcut_key = WXK_CONTROL_M; return true; @@ -73,22 +81,23 @@ bool GLGizmoMove3D::on_is_activable() const void GLGizmoMove3D::on_start_dragging() { - if (m_hover_id != -1) { - m_displacement = Vec3d::Zero(); - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_starting_drag_position = m_grabbers[m_hover_id].center; - m_starting_box_center = box.center(); - m_starting_box_bottom_center = box.center(); - m_starting_box_bottom_center(2) = box.min(2); - } + assert(m_hover_id != -1); + + m_displacement = Vec3d::Zero(); + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); + m_starting_drag_position = m_grabbers[m_hover_id].center; + m_starting_box_center = box.center(); + m_starting_box_bottom_center = box.center(); + m_starting_box_bottom_center(2) = box.min(2); } void GLGizmoMove3D::on_stop_dragging() { + m_parent.do_move(L("Gizmo-Move")); m_displacement = Vec3d::Zero(); } -void GLGizmoMove3D::on_update(const UpdateData& data) +void GLGizmoMove3D::on_dragging(const UpdateData& data) { if (m_hover_id == 0) m_displacement.x() = calc_projection(data); @@ -96,6 +105,9 @@ void GLGizmoMove3D::on_update(const UpdateData& data) m_displacement.y() = calc_projection(data); else if (m_hover_id == 2) m_displacement.z() = calc_projection(data); + + Selection &selection = m_parent.get_selection(); + selection.translate(m_displacement); } void GLGizmoMove3D::on_render() @@ -137,47 +149,63 @@ void GLGizmoMove3D::on_render() glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); - // draw grabbers - for (unsigned int i = 0; i < 3; ++i) { - if (m_grabbers[i].enabled) render_grabber_extension((Axis) i, box, false); - } + auto render_grabber_connection = [this, ¢er](unsigned int id) { + if (m_grabbers[id].enabled) { + //if (!m_grabber_connections[id].model.is_initialized() || !m_grabber_connections[id].old_center.isApprox(center)) { + m_grabber_connections[id].old_center = center; + m_grabber_connections[id].model.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = AXES_COLOR[id]; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)center.cast()); + init_data.add_vertex((Vec3f)m_grabbers[id].center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connections[id].model.init_from(std::move(init_data)); + //} - // draw axes line - // draw axes - for (unsigned int i = 0; i < 3; ++i) { - if (m_grabbers[i].enabled) { - glsafe(::glColor4fv(AXES_COLOR[i].data())); glLineStipple(1, 0x0FFF); glEnable(GL_LINE_STIPPLE); - ::glBegin(GL_LINES); - ::glVertex3dv(center.data()); - // use extension center - ::glVertex3dv(m_grabbers[i].center.data()); - glsafe(::glEnd()); + m_grabber_connections[id].model.render(); glDisable(GL_LINE_STIPPLE); } + }; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + // draw axes + for (unsigned int i = 0; i < 3; ++i) { + render_grabber_connection(i); + } + + shader->stop_using(); } + + // draw grabbers + render_grabbers(box); } -void GLGizmoMove3D::on_render_for_picking() +void GLGizmoMove3D::on_register_raycasters_for_picking() { - glsafe(::glDisable(GL_DEPTH_TEST)); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - //BBS donot render base grabber for picking - //render_grabbers_for_picking(box); - - //get picking colors only - for (unsigned int i = 0; i < (unsigned int) m_grabbers.size(); ++i) { - if (m_grabbers[i].enabled) { - std::array color = picking_color_component(i); - m_grabbers[i].color = color; - } - } - - render_grabber_extension(X, box, true); - render_grabber_extension(Y, box, true); - render_grabber_extension(Z, box, true); +void GLGizmoMove3D::on_unregister_raycasters_for_picking() +{ + m_parent.set_raycaster_gizmos_on_top(false); } //BBS: add input window for move @@ -192,17 +220,17 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; - double len_starting_vec = starting_vec.norm(); + const Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); + const Vec3d mouse_dir = data.mouse_ray.unit_vector(); // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; + const Vec3d inters_vec = inters - m_starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); @@ -213,51 +241,5 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const return projection; } - -void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const -{ -#if ENABLE_FIXED_GRABBER - float mean_size = (float)(GLGizmoBase::Grabber::FixedGrabberSize); -#else - float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); -#endif - - double size = 0.75 * GLGizmoBase::Grabber::FixedGrabberSize * GLGizmoBase::INV_ZOOM; - - std::array color = m_grabbers[axis].color; - if (!picking && m_hover_id != -1) { - if (m_hover_id == axis) { - color = m_grabbers[axis].hover_color; - } - } - - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - const_cast(&m_vbo_cone)->set_color(-1, color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_grabbers[axis].center.x(), m_grabbers[axis].center.y(), m_grabbers[axis].center.z())); - if (axis == X) - glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); - else if (axis == Y) - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - - //glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 2.0 * size)); - m_vbo_cone.render(); - glsafe(::glPopMatrix()); - - if (! picking) - shader->stop_using(); -} - - - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 21d19b4465..f4c04c730a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoMove_hpp_ #define slic3r_GLGizmoMove_hpp_ @@ -15,15 +19,18 @@ class GLGizmoMove3D : public GLGizmoBase { static const double Offset; - Vec3d m_displacement; + Vec3d m_displacement{ Vec3d::Zero() }; + double m_snap_step{ 1.0 }; + Vec3d m_starting_drag_position{ Vec3d::Zero() }; + Vec3d m_starting_box_center{ Vec3d::Zero() }; + Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; - double m_snap_step; - - Vec3d m_starting_drag_position; - Vec3d m_starting_box_center; - Vec3d m_starting_box_bottom_center; - - GLModel m_vbo_cone; + struct GrabberConnection + { + GLModel model; + Vec3d old_center{ Vec3d::Zero() }; + }; + std::array m_grabber_connections; //BBS: add size adjust related GizmoObjectManipulation* m_object_manipulation; @@ -37,25 +44,34 @@ public: double get_snap_step(double step) const { return m_snap_step; } void set_snap_step(double step) { m_snap_step = step; } - const Vec3d& get_displacement() const { return m_displacement; } - std::string get_tooltip() const override; + /// + /// Postpone to Grabber for move + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + /// + /// Detect reduction of move for wipetover on selection change + /// + void data_changed(bool is_serializing) override; protected: - virtual bool on_init() override; - virtual std::string on_get_name() const override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; - virtual void on_stop_dragging() override; - virtual void on_update(const UpdateData& data) override; - virtual void on_render() override; - virtual void on_render_for_picking() override; + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_dragging(const UpdateData& data) override; + void on_render() override; + void on_register_raycasters_for_picking() override; + void on_unregister_raycasters_for_picking() override; //BBS: GUI refactor: add object manipulation virtual void on_render_input_window(float x, float y, float bottom_limit); private: double calc_projection(const UpdateData& data) const; - void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index f682d249ec..6bf8a526ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -1,4 +1,7 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoPainterBase.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" @@ -8,6 +11,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -18,24 +22,28 @@ namespace Slic3r::GUI { +std::shared_ptr GLGizmoPainterBase::s_sphere = nullptr; GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - // Make sphere and save it into a vertex buffer. - m_vbo_sphere.load_its_flat_shading(its_make_sphere(1., (2*M_PI)/24.)); - m_vbo_sphere.finalize_geometry(true); m_vertical_only = false; m_horizontal_only = false; } -void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection) +GLGizmoPainterBase::~GLGizmoPainterBase() +{ + if (s_sphere != nullptr) + s_sphere.reset(); +} + +void GLGizmoPainterBase::data_changed(bool is_serializing) { if (m_state != On) return; const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; - + const Selection & selection = m_parent.get_selection(); if (mo && selection.is_from_single_instance() && (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size)) { @@ -101,8 +109,12 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const if (is_left_handed) glsafe(::glFrontFace(GL_CW)); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); // For printers with multiple extruders, it is necessary to pass trafo_matrix // to the shader input variable print_box.volume_world_matrix before @@ -110,16 +122,13 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const // wrong transformation matrix is used for "Clipping of view". shader->set_uniform("volume_world_matrix", trafo_matrix); - m_triangle_selectors[mesh_id]->render(m_imgui); - - glsafe(::glPopMatrix()); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); } } - -void GLGizmoPainterBase::render_cursor() const +void GLGizmoPainterBase::render_cursor() { // First check that the mouse pointer is on an object. const ModelObject* mo = m_c->selection_info()->model_object(); @@ -157,92 +166,127 @@ void GLGizmoPainterBase::render_cursor() const } } - - -void GLGizmoPainterBase::render_cursor_circle() const +void GLGizmoPainterBase::render_cursor_circle() { - const Camera &camera = wxGetApp().plater()->get_camera(); - auto zoom = (float) camera.get_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - float cnv_half_width = 0.5f * (float) cnv_size.get_width(); - float cnv_half_height = 0.5f * (float) cnv_size.get_height(); - if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) + const Size cnv_size = m_parent.get_canvas_size(); + const float cnv_width = float(cnv_size.get_width()); + const float cnv_height = float(cnv_size.get_height()); + if (cnv_width == 0.0f || cnv_height == 0.0f) return; - Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); - Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1)); - center = center * inv_zoom; + + const float cnv_inv_width = 1.0f / cnv_width; + const float cnv_inv_height = 1.0f / cnv_height; + + const Vec2d center = m_parent.get_local_mouse_position(); + const float radius = m_cursor_radius * float(wxGetApp().plater()->get_camera().get_zoom()); glsafe(::glLineWidth(1.5f)); - - // BBS - std::array render_color = this->get_cursor_hover_color(); - if (m_button_down == Button::Left) - render_color = this->get_cursor_sphere_left_button_color(); - else if (m_button_down == Button::Right) - render_color = this->get_cursor_sphere_right_button_color(); - glsafe(::glColor4fv(render_color.data())); - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - // ensure that the circle is renderered inside the frustrum - glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); - // ensure that the overlay fits the frustrum near z plane - double gui_scale = camera.get_gui_scale(); - glsafe(::glScaled(gui_scale, gui_scale, 1.0)); - glsafe(::glPushAttrib(GL_ENABLE_BIT)); glsafe(::glLineStipple(4, 0xAAAA)); glsafe(::glEnable(GL_LINE_STIPPLE)); - ::glBegin(GL_LINE_LOOP); - for (double angle=0; angle<2*M_PI; angle+=M_PI/20.) - ::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle))); - glsafe(::glEnd()); + if (!m_circle.is_initialized() || !m_old_center.isApprox(center) || std::abs(m_old_cursor_radius - radius) > EPSILON) { + m_old_cursor_radius = radius; + m_old_center = center; + m_circle.reset(); + + GLModel::Geometry init_data; + static const unsigned int StepsCount = 32; + static const float StepSize = 2.0f * float(PI) / float(StepsCount); + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P2 }; + init_data.color = { 0.0f, 1.0f, 0.3f, 1.0f }; + init_data.reserve_vertices(StepsCount); + init_data.reserve_indices(StepsCount); + + // vertices + indices + for (unsigned int i = 0; i < StepsCount; ++i) { + const float angle = float(i * StepSize); + init_data.add_vertex(Vec2f(2.0f * ((center.x() + ::cos(angle) * radius) * cnv_inv_width - 0.5f), + -2.0f * ((center.y() + ::sin(angle) * radius) * cnv_inv_height - 0.5f))); + init_data.add_index(i); + } + + m_circle.init_from(std::move(init_data)); + } + + // BBS + ColorRGBA render_color = this->get_cursor_hover_color(); + if (m_button_down == Button::Left) + render_color = this->get_cursor_sphere_left_button_color(); + else if (m_button_down == Button::Right) + render_color = this->get_cursor_sphere_right_button_color(); + + m_circle.set_color(render_color); + + GLShaderProgram* shader = GUI::wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + m_circle.render(); + shader->stop_using(); + } glsafe(::glPopAttrib()); - glsafe(::glPopMatrix()); glsafe(::glEnable(GL_DEPTH_TEST)); } void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const { + if (s_sphere == nullptr) { + s_sphere = std::make_shared(); + s_sphere->init_from(its_make_sphere(1.0, double(PI) / 12.0)); + } + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix(true, true, false, true).inverse(); - const bool is_left_handed = Geometry::Transformation(trafo).is_left_handed(); - - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo.data())); - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glTranslatef(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))); - glsafe(::glMultMatrixd(complete_scaling_matrix_inverse.data())); - glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius)); - - if (is_left_handed) - glFrontFace(GL_CW); // BBS - std::array render_color = this->get_cursor_hover_color(); + ColorRGBA render_color = this->get_cursor_hover_color(); if (m_button_down == Button::Left) render_color = this->get_cursor_sphere_left_button_color(); else if (m_button_down == Button::Right) render_color = this->get_cursor_sphere_right_button_color(); - glsafe(::glColor4fv(render_color.data())); - m_vbo_sphere.render(); + shader->start_using(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * trafo * + Geometry::assemble_transform(m_rr.hit.cast()) * complete_scaling_matrix_inverse * + Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_cursor_radius * Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const bool is_left_handed = Geometry::Transformation(view_model_matrix).is_left_handed(); + if (is_left_handed) + glsafe(::glFrontFace(GL_CW)); + + assert(s_sphere != nullptr); + s_sphere->set_color(render_color); + s_sphere->render(); if (is_left_handed) - glFrontFace(GL_CCW); + glsafe(::glFrontFace(GL_CCW)); - glsafe(::glPopMatrix()); + shader->stop_using(); } // BBS void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) const { + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + const BoundingBoxf3 box = bounding_box(); Vec3d hit_world = trafo * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)); float max_z = (float)box.max.z(); @@ -268,14 +312,18 @@ void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) co for (int i = 0; i < zs.size(); i++) { update_contours(vol_mesh, zs[i], max_z, min_z); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(m_cut_contours.shift); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); glsafe(::glLineWidth(2.0f)); m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); } } + + shader->stop_using(); } BoundingBoxf3 GLGizmoPainterBase::bounding_box() const @@ -294,7 +342,7 @@ BoundingBoxf3 GLGizmoPainterBase::bounding_box() const void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const { const Selection& selection = m_parent.get_selection(); - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* first_glvolume = selection.get_first_volume(); const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; @@ -317,7 +365,7 @@ void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cur const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params); if (!polys.empty()) { m_cut_contours.contours.init_from(polys, static_cast(cursor_z)); - m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); + m_cut_contours.contours.set_color({ 1.0f, 1.0f, 1.0f, 1.0f }); } } else if (box.center() != m_cut_contours.position) { @@ -584,13 +632,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous pos = action == SLAGizmoEventType::MouseWheelDown ? std::max(0., pos - 0.01) : std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } @@ -741,14 +789,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous assert(m_cursor_type == TriangleSelector::CursorType::CIRCLE || m_cursor_type == TriangleSelector::CursorType::SPHERE); if (projected_mouse_positions.size() == 1) { - const ProjectedMousePosition& first_position = projected_mouse_positions.front(); - std::unique_ptr cursor = TriangleSelector::SinglePointCursor::cursor_factory(first_position.mesh_hit, - camera_pos, m_cursor_radius, - m_cursor_type, trafo_matrix, clp); + const ProjectedMousePosition &first_position = projected_mouse_positions.front(); + std::unique_ptr cursor = TriangleSelector::SinglePointCursor::cursor_factory(first_position.mesh_hit, + camera_pos, m_cursor_radius, + m_cursor_type, trafo_matrix, clp); m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, - m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); - } - else { + m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); + } else { for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) { auto second_position_it = first_position_it + 1; std::unique_ptr cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp); @@ -853,7 +900,72 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return false; } +bool GLGizmoPainterBase::on_mouse(const wxMouseEvent &mouse_event) +{ + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + if (mouse_event.Moving()) { + gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false); + return false; + } + + // when control is down we allow scene pan and rotation even when clicking + // over some object + bool control_down = mouse_event.CmdDown(); + bool grabber_contains_mouse = (get_hover_id() != -1); + + const Selection &selection = m_parent.get_selection(); + int selected_object_idx = selection.get_object_idx(); + if (mouse_event.LeftDown()) { + if ((!control_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + // the gizmo got the event and took some action, there is no need + // to do anything more + return true; + } else if (mouse_event.RightDown()){ + if (!control_down && selected_object_idx != -1 && + gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) + // event was taken care of + return true; + } else if (mouse_event.Dragging()) { + if (m_parent.get_move_volume_id() != -1) + // don't allow dragging objects with the Sla gizmo on + return true; + if (!control_down && gizmo_event(SLAGizmoEventType::Dragging, + mouse_pos, mouse_event.ShiftDown(), + mouse_event.AltDown(), false)) { + // the gizmo got the event and took some action, no need to do + // anything more here + m_parent.set_as_dirty(); + return true; + } + if(control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) + { + // CTRL has been pressed while already dragging -> stop current action + if (mouse_event.LeftIsDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); + else if (mouse_event.RightIsDown()) + gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); + return false; + } + } else if (mouse_event.LeftUp()) { + if (!m_parent.is_mouse_dragging()) { + // in case SLA/FDM gizmo is selected, we just pass the LeftUp + // event and stop processing - neither object moving or selecting + // is suppressed in that case + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down); + return true; + } + } else if (mouse_event.RightUp()) { + if (!m_parent.is_mouse_dragging()) { + gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down); + return true; + } + } + return false; +} void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position, const Camera& camera, @@ -1002,17 +1114,20 @@ TriangleSelector::ClippingPlane GLGizmoPainterBase::get_clipping_plane_in_volume return TriangleSelector::ClippingPlane({float(normal_transformed.x()), float(normal_transformed.y()), float(normal_transformed.z()), offset_transformed}); } -std::array TriangleSelectorGUI::get_seed_fill_color(const std::array &base_color) +ColorRGBA TriangleSelectorGUI::enforcers_color = {0.5f, 1.f, 0.5f, 1.f}; +ColorRGBA TriangleSelectorGUI::blockers_color = {1.f, 0.5f, 0.5f, 1.f}; + +ColorRGBA TriangleSelectorGUI::get_seed_fill_color(const ColorRGBA& base_color) { // BBS return { - base_color[0] * 1.25f < 1.f ? base_color[0] * 1.25f : 1.f, - base_color[1] * 1.25f < 1.f ? base_color[1] * 1.25f : 1.f, - base_color[2] * 1.25f < 1.f ? base_color[2] * 1.25f : 1.f, + base_color.r() * 1.25f < 1.f ? base_color.r() * 1.25f : 1.f, + base_color.g() * 1.25f < 1.f ? base_color.g() * 1.25f : 1.f, + base_color.b() * 1.25f < 1.f ? base_color.b() * 1.25f : 1.f, 1.f}; } -void TriangleSelectorGUI::render(ImGuiWrapper* imgui) +void TriangleSelectorGUI::render(ImGuiWrapper* imgui, const Transform3d& matrix) { if (m_update_render_data) { update_render_data(); @@ -1022,41 +1137,25 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) auto* shader = wxGetApp().get_current_shader(); if (! shader) return; - assert(shader->get_name() == "gouraud"); - ScopeGuard guard([shader]() { if (shader) shader->set_uniform("offset_depth_buffer", false);}); - shader->set_uniform("offset_depth_buffer", true); + assert(shader->get_name() == "gouraud" || shader->get_name() == "mm_gouraud"); + for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color), std::make_pair(&m_iva_blockers, blockers_color)}) { - if (iva.first->has_VBOs()) { - shader->set_uniform("uniform_color", iva.second); - iva.first->render(); - } + iva.first->set_color(iva.second); + iva.first->render(); } - for (auto &iva : m_iva_seed_fills) - if (iva.has_VBOs()) { - size_t color_idx = &iva - &m_iva_seed_fills.front(); - const std::array &color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : - color_idx == 2 ? blockers_color : - GLVolume::NEUTRAL_COLOR); - shader->set_uniform("uniform_color", color); - iva.render(); - } - - if (m_paint_contour.has_VBO()) { - ScopeGuard guard_gouraud([shader]() { shader->start_using(); }); - shader->stop_using(); - - auto *contour_shader = wxGetApp().get_shader("mm_contour"); - contour_shader->start_using(); - - glsafe(::glDepthFunc(GL_LEQUAL)); - m_paint_contour.render(); - glsafe(::glDepthFunc(GL_LESS)); - - contour_shader->stop_using(); + for (auto& iva : m_iva_seed_fills) { + size_t color_idx = &iva - &m_iva_seed_fills.front(); + const ColorRGBA& color = TriangleSelectorGUI::get_seed_fill_color(color_idx == 1 ? enforcers_color : + color_idx == 2 ? blockers_color : + GLVolume::NEUTRAL_COLOR); + iva.set_color(color); + iva.render(); } + render_paint_contour(matrix); + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) render_debug(imgui); @@ -1071,24 +1170,33 @@ void TriangleSelectorGUI::update_render_data() int blc_cnt = 0; std::vector seed_fill_cnt(m_iva_seed_fills.size(), 0); - for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) - iva->release_geometry(); + for (auto* iva : { &m_iva_enforcers, &m_iva_blockers }) { + iva->reset(); + } - for (auto &iva : m_iva_seed_fills) - iva.release_geometry(); + for (auto& iva : m_iva_seed_fills) { + iva.reset(); + } + + GLModel::Geometry iva_enforcers_data; + iva_enforcers_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + GLModel::Geometry iva_blockers_data; + iva_blockers_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + std::array iva_seed_fills_data; + for (auto& data : iva_seed_fills_data) + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + + // small value used to offset triangles along their normal to avoid z-fighting + static const float offset = 0.001f; for (const Triangle &tr : m_triangles) { - bool is_valid = tr.valid(); - bool is_split = tr.is_split(); - EnforcerBlockerType type = tr.get_state(); - bool is_select_by_seed_fill = tr.is_selected_by_seed_fill(); if (!tr.valid() || tr.is_split() || (tr.get_state() == EnforcerBlockerType::NONE && !tr.is_selected_by_seed_fill())) continue; int tr_state = int(tr.get_state()); - GLIndexedVertexArray &iva = tr.is_selected_by_seed_fill() ? m_iva_seed_fills[tr_state] : - tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : - m_iva_blockers; + GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] : + tr.get_state() == EnforcerBlockerType::ENFORCER ? iva_enforcers_data : + iva_blockers_data; int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] : tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt; @@ -1098,37 +1206,25 @@ void TriangleSelectorGUI::update_render_data() //FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort // or the current implementation may be more cache friendly. const Vec3f n = (v1 - v0).cross(v2 - v1).normalized(); - iva.push_geometry(v0, n); - iva.push_geometry(v1, n); - iva.push_geometry(v2, n); - iva.push_triangle(cnt, cnt + 1, cnt + 2); + // small value used to offset triangles along their normal to avoid z-fighting + const Vec3f offset_n = offset * n; + iva.add_vertex(v0 + offset_n, n); + iva.add_vertex(v1 + offset_n, n); + iva.add_vertex(v2 + offset_n, n); + iva.add_triangle((unsigned int)cnt, (unsigned int)cnt + 1, (unsigned int)cnt + 2); cnt += 3; } - for (auto *iva : {&m_iva_enforcers, &m_iva_blockers}) - iva->finalize_geometry(true); - - for (auto &iva : m_iva_seed_fills) - iva.finalize_geometry(true); - - m_paint_contour.release_geometry(); - std::vector contour_edges = this->get_seed_fill_contour(); - m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6); - for (const Vec2i &edge : contour_edges) { - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.x()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.y()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.z()); - - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.x()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.y()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.z()); + if (!iva_enforcers_data.is_empty()) + m_iva_enforcers.init_from(std::move(iva_enforcers_data)); + if (!iva_blockers_data.is_empty()) + m_iva_blockers.init_from(std::move(iva_blockers_data)); + for (size_t i = 0; i < m_iva_seed_fills.size(); ++i) { + if (!iva_seed_fills_data[i].is_empty()) + m_iva_seed_fills[i].init_from(std::move(iva_seed_fills_data[i])); } - m_paint_contour.contour_indices.assign(m_paint_contour.contour_vertices.size() / 3, 0); - std::iota(m_paint_contour.contour_indices.begin(), m_paint_contour.contour_indices.end(), 0); - m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size(); - - m_paint_contour.finalize_geometry(); + update_paint_contour(); } // BBS @@ -1139,68 +1235,39 @@ bool TrianglePatch::is_fragment() const float TriangleSelectorPatch::gap_area = TriangleSelectorPatch::GapAreaMin; -void TriangleSelectorPatch::render(ImGuiWrapper* imgui) +void TriangleSelectorPatch::render(ImGuiWrapper* imgui, const Transform3d& matrix) { - if (m_update_render_data) + if (m_update_render_data) { update_render_data(); + m_update_render_data = false; + } auto* shader = wxGetApp().get_current_shader(); if (!shader) return; - assert(shader->get_name() == "mm_gouraud"); - GLint position_id = -1; - GLint barycentric_id = -1; - if (wxGetApp().plater()->is_wireframe_enabled()) { - position_id = shader->get_attrib_location("v_position"); - barycentric_id = shader->get_attrib_location("v_barycentric"); - if (m_need_wireframe && wxGetApp().plater()->is_show_wireframe()) { - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", show_wireframe on"); - shader->set_uniform("show_wireframe", true); - } - else { - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", show_wireframe off"); - shader->set_uniform("show_wireframe", false); - } - } + assert(shader->get_name() == "gouraud" || shader->get_name() == "mm_gouraud"); for (size_t buffer_idx = 0; buffer_idx < m_triangle_patches.size(); ++buffer_idx) { if (this->has_VBOs(buffer_idx)) { const TrianglePatch& patch = m_triangle_patches[buffer_idx]; - std::array color; + ColorRGBA color; if (patch.is_fragment() && !patch.neighbor_types.empty()) { size_t color_idx = (size_t)*patch.neighbor_types.begin(); color = m_ebt_colors[color_idx]; - color[3] = 0.85; + color.a(0.85); } else { size_t color_idx = (size_t)patch.type; color = m_ebt_colors[color_idx]; } //to make black not too hard too see - std::array new_color = adjust_color_for_rendering(color); + ColorRGBA new_color = adjust_color_for_rendering(color); shader->set_uniform("uniform_color", new_color); - //shader->set_uniform("uniform_color", color); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", buffer_idx %1%: new_color[%2%, %3%, %4%, %5%]")%buffer_idx%new_color[0]%new_color[1]%new_color[2]%new_color[3]; - this->render(buffer_idx, (int)position_id, (int)barycentric_id); + this->render(buffer_idx); } } - if (m_paint_contour.has_VBO()) - { - ScopeGuard guard_mm_gouraud([shader]() { shader->start_using(); }); - shader->stop_using(); - - auto* contour_shader = wxGetApp().get_shader("mm_contour"); - contour_shader->start_using(); - - glsafe(::glDepthFunc(GL_LEQUAL)); - m_paint_contour.render(); - glsafe(::glDepthFunc(GL_LESS)); - - contour_shader->stop_using(); - } - - m_update_render_data = false; + render_paint_contour(matrix); } void TriangleSelectorPatch::update_triangles_per_type() @@ -1423,59 +1490,32 @@ void TriangleSelectorPatch::update_render_data() } //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", before paint_contour"); - - m_paint_contour.release_geometry(); - std::vector contour_edges = this->get_seed_fill_contour(); - m_paint_contour.contour_vertices.reserve(contour_edges.size() * 6); - for (const Vec2i& edge : contour_edges) { - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.x()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.y()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(0)].v.z()); - - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.x()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.y()); - m_paint_contour.contour_vertices.emplace_back(m_vertices[edge(1)].v.z()); - } - - m_paint_contour.contour_indices.assign(m_paint_contour.contour_vertices.size() / 3, 0); - std::iota(m_paint_contour.contour_indices.begin(), m_paint_contour.contour_indices.end(), 0); - m_paint_contour.contour_indices_size = m_paint_contour.contour_indices.size(); - - m_paint_contour.finalize_geometry(); + update_paint_contour(); //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", exit"); } -void TriangleSelectorPatch::render(int triangle_indices_idx, int position_id, int barycentric_id) +void TriangleSelectorPatch::render(int triangle_indices_idx) { assert(triangle_indices_idx < this->m_triangle_indices_VBO_ids.size()); assert(this->m_triangle_patches.size() == this->m_triangle_indices_VBO_ids.size()); //assert(this->m_vertices_VBO_id != 0); assert(this->m_triangle_patches.size() == this->m_vertices_VBO_ids.size()); + assert(this->m_vertices_VAO_ids[triangle_indices_idx] != 0); assert(this->m_vertices_VBO_ids[triangle_indices_idx] != 0); assert(this->m_triangle_indices_VBO_ids[triangle_indices_idx] != 0); - //glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_vertices_VBO_id)); - //glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float)))); - if (this->m_triangle_indices_sizes[triangle_indices_idx] > 0) { - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_vertices_VBO_ids[triangle_indices_idx])); - if (position_id != -1) { - glsafe(::glEnableVertexAttribArray((GLint)position_id)); - glsafe(::glVertexAttribPointer((GLint)position_id, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), nullptr)); - } - else { - glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr)); - } + GLShaderProgram *shader = wxGetApp().get_current_shader(); + if (shader == nullptr) + return; - if (barycentric_id != -1) { - glsafe(::glEnableVertexAttribArray((GLint)barycentric_id)); - glsafe(::glVertexAttribPointer((GLint)barycentric_id, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (GLvoid*)(intptr_t)(3 * sizeof(float)))); - } - //glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr)); - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: triangle_indices_idx %2%, bind vertex vbo, buffer id %3%")%__LINE__%triangle_indices_idx%this->m_vertices_VBO_ids[triangle_indices_idx]; + // the following binding is needed to set the vertex attributes + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_vertices_VBO_ids[triangle_indices_idx])); + const GLint position_id = shader->get_attrib_location("v_position"); + if (position_id != -1) { + glsafe(::glVertexAttribPointer((GLint) position_id, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr)); + glsafe(::glEnableVertexAttribArray((GLint)position_id)); } - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - // Render using the Vertex Buffer Objects. if (this->m_triangle_indices_sizes[triangle_indices_idx] > 0) { glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_triangle_indices_VBO_ids[triangle_indices_idx])); @@ -1484,14 +1524,10 @@ void TriangleSelectorPatch::render(int triangle_indices_idx, int position_id, in //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: triangle_indices_idx %2%, bind indices vbo, buffer id %3%")%__LINE__%triangle_indices_idx%this->m_triangle_indices_VBO_ids[triangle_indices_idx]; } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - if ((this->m_triangle_indices_sizes[triangle_indices_idx] > 0)&&(position_id != -1)) + if (position_id != -1) glsafe(::glDisableVertexAttribArray(position_id)); - if ((this->m_triangle_indices_sizes[triangle_indices_idx] > 0)&&(barycentric_id != -1)) - glsafe(::glDisableVertexAttribArray((GLint)barycentric_id)); - if (this->m_triangle_indices_sizes[triangle_indices_idx] > 0) - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } void TriangleSelectorPatch::release_geometry() @@ -1556,64 +1592,6 @@ void TriangleSelectorPatch::finalize_triangle_indices() } } -void GLPaintContour::render() const -{ - assert(this->m_contour_VBO_id != 0); - assert(this->m_contour_EBO_id != 0); - - glsafe(::glLineWidth(4.0f)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_VBO_id)); - glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr)); - - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - - if (this->contour_indices_size > 0) { - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_contour_EBO_id)); - glsafe(::glDrawElements(GL_LINES, GLsizei(this->contour_indices_size), GL_UNSIGNED_INT, nullptr)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - } - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); -} - -void GLPaintContour::finalize_geometry() -{ - assert(this->m_contour_VBO_id == 0); - assert(this->m_contour_EBO_id == 0); - - if (!this->contour_vertices.empty()) { - glsafe(::glGenBuffers(1, &this->m_contour_VBO_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_VBO_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_vertices.size() * sizeof(float), this->contour_vertices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - this->contour_vertices.clear(); - } - - if (!this->contour_indices.empty()) { - glsafe(::glGenBuffers(1, &this->m_contour_EBO_id)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_contour_EBO_id)); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - this->contour_indices.clear(); - } -} - -void GLPaintContour::release_geometry() -{ - if (this->m_contour_VBO_id) { - glsafe(::glDeleteBuffers(1, &this->m_contour_VBO_id)); - this->m_contour_VBO_id = 0; - } - if (this->m_contour_EBO_id) { - glsafe(::glDeleteBuffers(1, &this->m_contour_EBO_id)); - this->m_contour_EBO_id = 0; - } - this->clear(); -} - #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) { @@ -1650,59 +1628,124 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui) }; for (auto& va : m_varrays) - va.release_geometry(); + va.reset(); std::array cnts; ::glScalef(1.01f, 1.01f, 1.01f); + std::array varrays_data; + for (auto& data : varrays_data) + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + for (int tr_id=0; tr_idpush_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), - double(m_vertices[tr.verts_idxs[i]].v[1]), - double(m_vertices[tr.verts_idxs[i]].v[2]), - 0., 0., 1.); - va->push_triangle(*cnt, - *cnt+1, - *cnt+2); + for (int i = 0; i < 3; ++i) { + va->add_vertex(m_vertices[tr.verts_idxs[i]].v, Vec3f(0.0f, 0.0f, 1.0f)); + } + va->add_uint_triangle((unsigned int)*cnt, (unsigned int)*cnt + 1, (unsigned int)*cnt + 2); *cnt += 3; } + for (int i = 0; i < 3; ++i) { + if (!varrays_data[i].is_empty()) + m_varrays[i].init_from(std::move(varrays_data[i])); + } + + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + ::glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); for (vtype i : {ORIGINAL, SPLIT, INVALID}) { - GLIndexedVertexArray& va = m_varrays[i]; - va.finalize_geometry(true); - if (va.has_VBOs()) { - switch (i) { - case ORIGINAL : ::glColor3f(0.f, 0.f, 1.f); break; - case SPLIT : ::glColor3f(1.f, 0.f, 0.f); break; - case INVALID : ::glColor3f(1.f, 1.f, 0.f); break; - } - va.render(); + GLModel& va = m_varrays[i]; + switch (i) { + case ORIGINAL: va.set_color({ 0.0f, 0.0f, 1.0f, 1.0f }); break; + case SPLIT: va.set_color({ 1.0f, 0.0f, 0.0f, 1.0f }); break; + case INVALID: va.set_color({ 1.0f, 1.0f, 0.0f, 1.0f }); break; } + va.render(); } ::glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); } -#endif +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +void TriangleSelectorGUI::update_paint_contour() +{ + m_paint_contour.reset(); + GLModel::Geometry init_data; + const std::vector contour_edges = this->get_seed_fill_contour(); + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * contour_edges.size()); + init_data.reserve_indices(2 * contour_edges.size()); + init_data.color = ColorRGBA::WHITE(); + // vertices + indices + unsigned int vertices_count = 0; + for (const Vec2i& edge : contour_edges) { + init_data.add_vertex(m_vertices[edge(0)].v); + init_data.add_vertex(m_vertices[edge(1)].v); + vertices_count += 2; + init_data.add_line(vertices_count - 2, vertices_count - 1); + } + + if (!init_data.is_empty()) + m_paint_contour.init_from(std::move(init_data)); +} + +void TriangleSelectorGUI::render_paint_contour(const Transform3d& matrix) +{ + auto* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + auto* contour_shader = wxGetApp().get_shader("mm_contour"); + if (contour_shader != nullptr) { + contour_shader->start_using(); + + contour_shader->set_uniform("offset", OpenGLManager::get_gl_info().is_mesa() ? 0.0005 : 0.00001); + const Camera& camera = wxGetApp().plater()->get_camera(); + contour_shader->set_uniform("view_model_matrix", camera.get_view_matrix() * matrix); + contour_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + m_paint_contour.render(); + + contour_shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +} } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index d2f69ad2ae..4ea9412ffa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -1,9 +1,13 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Pavel Mikuš @Godrak, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoPainterBase_hpp_ #define slic3r_GLGizmoPainterBase_hpp_ #include "GLGizmoBase.hpp" -#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLModel.hpp" #include "libslic3r/ObjectID.hpp" #include "libslic3r/TriangleSelector.hpp" @@ -12,6 +16,7 @@ #include #include +#include namespace Slic3r::GUI { @@ -20,6 +25,7 @@ enum class SLAGizmoEventType : unsigned char; class ClippingPlane; struct Camera; class GLGizmoMmuSegmentation; +class Selection; enum class PainterGizmoType { FDM_SUPPORTS, @@ -27,52 +33,14 @@ enum class PainterGizmoType { MMU_SEGMENTATION }; -class GLPaintContour -{ -public: - GLPaintContour() = default; - - void render() const; - - inline bool has_VBO() const { return this->m_contour_EBO_id != 0; } - - // Release the geometry data, release OpenGL VBOs. - void release_geometry(); - - // Finalize the initialization of the contour geometry and the indices, upload both to OpenGL VBO objects - // and possibly releasing it if it has been loaded into the VBOs. - void finalize_geometry(); - - void clear() - { - this->contour_vertices.clear(); - this->contour_indices.clear(); - this->contour_indices_size = 0; - } - - std::vector contour_vertices; - std::vector contour_indices; - - // When the triangle indices are loaded into the graphics card as Vertex Buffer Objects, - // the above mentioned std::vectors are cleared and the following variables keep their original length. - size_t contour_indices_size{0}; - - // IDs of the Vertex Array Objects, into which the geometry has been loaded. - // Zero if the VBOs are not sent to GPU yet. - GLuint m_contour_VBO_id{0}; - GLuint m_contour_EBO_id{0}; -}; - class TriangleSelectorGUI : public TriangleSelector { public: explicit TriangleSelectorGUI(const TriangleMesh& mesh, float edge_limit = 0.6f) : TriangleSelector(mesh, edge_limit) {} virtual ~TriangleSelectorGUI() = default; - // Render current selection. Transformation matrices are supposed - // to be already set. - virtual void render(ImGuiWrapper *imgui); - void render() { this->render(nullptr); } + virtual void render(ImGuiWrapper* imgui, const Transform3d& matrix); + //void render(const Transform3d& matrix) { this->render(nullptr, matrix); } void set_wireframe_needed(bool need_wireframe) { m_need_wireframe = need_wireframe; } bool get_wireframe_needed() { return m_need_wireframe; } @@ -81,11 +49,11 @@ public: { m_update_render_data = true; m_paint_changed |= paint_changed; - }; + } // BBS - static constexpr std::array enforcers_color{ 0.5f, 1.f, 0.5f, 1.f }; - static constexpr std::array blockers_color{ 1.f, 0.5f, 0.5f, 1.f }; + static ColorRGBA enforcers_color; + static ColorRGBA blockers_color; #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); @@ -98,18 +66,24 @@ protected: // BBS bool m_paint_changed = true; - static std::array get_seed_fill_color(const std::array &base_color); + static ColorRGBA get_seed_fill_color(const ColorRGBA &base_color); private: void update_render_data(); - GLIndexedVertexArray m_iva_enforcers; - GLIndexedVertexArray m_iva_blockers; - std::array m_iva_seed_fills; - std::array m_varrays; + GLModel m_iva_enforcers; + GLModel m_iva_blockers; + std::array m_iva_seed_fills; +#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG + std::array m_varrays; +#endif // PRUSASLICER_TRIANGLE_SELECTOR_DEBUG protected: - GLPaintContour m_paint_contour; + GLModel m_paint_contour; + + void update_paint_contour(); + void render_paint_contour(const Transform3d& matrix); + bool m_need_wireframe {false}; }; @@ -128,20 +102,20 @@ struct TrianglePatch { class TriangleSelectorPatch : public TriangleSelectorGUI { public: - explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector> ebt_colors, float edge_limit = 0.6f) + explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector ebt_colors, float edge_limit = 0.6f) : TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) {} virtual ~TriangleSelectorPatch() = default; // Render current selection. Transformation matrices are supposed // to be already set. - void render(ImGuiWrapper* imgui) override; + void render(ImGuiWrapper* imgui, const Transform3d& matrix) override; // TriangleSelector.m_triangles => m_gizmo_scene.triangle_patches void update_triangles_per_type(); // m_gizmo_scene.triangle_patches => TriangleSelector.m_triangles void update_selector_triangles(); void update_triangles_per_patch(); - void set_ebt_colors(const std::vector> ebt_colors) { m_ebt_colors = ebt_colors; } + void set_ebt_colors(const std::vector ebt_colors) { m_ebt_colors = ebt_colors; } void set_filter_state(bool is_filter_state); constexpr static float GapAreaMin = 0.f; @@ -195,13 +169,13 @@ protected: std::vector m_vertices_VBO_ids; std::vector m_triangle_indices_VBO_ids; - std::vector> m_ebt_colors; + std::vector m_ebt_colors; bool m_filter_state = false; private: void update_render_data(); - void render(int buffer_idx, int position_id = -1, int barycentric_id = -1); + void render(int buffer_idx); }; @@ -214,18 +188,18 @@ private: ObjectID m_old_mo_id; size_t m_old_volumes_size = 0; void on_render() override {} - void on_render_for_picking() override {} + public: GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - ~GLGizmoPainterBase() override = default; - virtual void set_painter_gizmo_data(const Selection& selection); + ~GLGizmoPainterBase() override; + void data_changed(bool is_serializing) override; virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); // Following function renders the triangles and cursor. Having this separated // from usual on_render method allows to render them before transparent // objects, so they can be seen inside them. The usual on_render is called // after all volumes (including transparent ones) are rendered. - virtual void render_painter_gizmo() const = 0; + virtual void render_painter_gizmo() = 0; virtual const float get_cursor_radius_min() const { return CursorRadiusMin; } virtual const float get_cursor_radius_max() const { return CursorRadiusMax; } @@ -236,10 +210,19 @@ public: virtual const float get_cursor_height_max() const { return CursorHeightMax; } virtual const float get_cursor_height_step() const { return CursorHeightStep; } + /// + /// Implement when want to process mouse events in gizmo + /// Click, Right click, move, drag, ... + /// + /// Keep information about mouse click + /// Return True when use the information and don't want to + /// propagate it otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + protected: virtual void render_triangles(const Selection& selection) const; - void render_cursor() const; - void render_cursor_circle() const; + void render_cursor(); + void render_cursor_circle(); void render_cursor_sphere(const Transform3d& trafo) const; // BBS void render_cursor_height_range(const Transform3d& trafo) const; @@ -247,10 +230,10 @@ protected: virtual void update_model_object() = 0; virtual void update_from_model_object(bool first_update) = 0; - virtual std::array get_cursor_sphere_left_button_color() const { return {0.f, 0.f, 1.f, 0.25f}; } - virtual std::array get_cursor_sphere_right_button_color() const { return {1.f, 0.f, 0.f, 0.25f}; } + virtual ColorRGBA get_cursor_sphere_left_button_color() const { return { 0.0f, 0.0f, 1.0f, 0.25f }; } + virtual ColorRGBA get_cursor_sphere_right_button_color() const { return { 1.0f, 0.0f, 0.0f, 0.25f }; } // BBS - virtual std::array get_cursor_hover_color() const { return { 0.f, 0.f, 0.f, 0.25f }; } + virtual ColorRGBA get_cursor_hover_color() const { return { 0.f, 0.f, 0.f, 0.25f }; } virtual EnforcerBlockerType get_left_button_state_type() const { return EnforcerBlockerType::ENFORCER; } virtual EnforcerBlockerType get_right_button_state_type() const { return EnforcerBlockerType::BLOCKER; } @@ -300,6 +283,9 @@ protected: bool m_paint_on_overhangs_only = false; float m_highlight_by_angle_threshold_deg = 0.f; + GLModel m_circle; + Vec2d m_old_center{ Vec2d::Zero() }; + float m_old_cursor_radius{ 0.0f }; static constexpr float SmartFillAngleMin = 0.0f; static constexpr float SmartFillAngleMax = 90.f; static constexpr float SmartFillAngleStep = 1.f; @@ -338,7 +324,7 @@ private: const Camera& camera, const std::vector& trafo_matrices) const; - GLIndexedVertexArray m_vbo_sphere; + static std::shared_ptr s_sphere; bool m_internal_stack_active = false; bool m_schedule_update = false; @@ -356,7 +342,7 @@ private: Vec3f hit; size_t facet; }; - mutable RaycastResult m_rr; + mutable RaycastResult m_rr = {Vec2d::Zero(), -1, Vec3f::Zero(), 0}; // BBS struct CutContours @@ -376,9 +362,6 @@ private: protected: void on_set_state() override; - void on_start_dragging() override {} - void on_stop_dragging() override {} - virtual void on_opening() = 0; virtual void on_shutdown() = 0; virtual PainterGizmoType get_painter_type() const = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 65479b064e..1b412c847c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -1,4 +1,7 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoRotate.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" @@ -17,10 +20,9 @@ namespace GUI { const float GLGizmoRotate::Offset = 5.0f; -const unsigned int GLGizmoRotate::CircleResolution = 64; const unsigned int GLGizmoRotate::AngleResolution = 64; const unsigned int GLGizmoRotate::ScaleStepsCount = 72; -const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; +const float GLGizmoRotate::ScaleStepRad = 2.0f * float(PI) / GLGizmoRotate::ScaleStepsCount; const unsigned int GLGizmoRotate::ScaleLongEvery = 2; const float GLGizmoRotate::ScaleLongTooth = 0.1f; // in percent of radius const unsigned int GLGizmoRotate::SnapRegionsCount = 8; @@ -30,33 +32,20 @@ const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) : GLGizmoBase(parent, "", -1) , m_axis(axis) - , m_angle(0.0) - , m_center(0.0, 0.0, 0.0) - , m_radius(0.0f) - , m_snap_coarse_in_radius(0.0f) - , m_snap_coarse_out_radius(0.0f) - , m_snap_fine_in_radius(0.0f) - , m_snap_fine_out_radius(0.0f) + , m_drag_color(DEFAULT_DRAG_COLOR) + , m_highlight_color(DEFAULT_HIGHLIGHT_COLOR) { + m_group_id = static_cast(axis); } -GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) - : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id) - , m_axis(other.m_axis) - , m_angle(other.m_angle) - , m_center(other.m_center) - , m_radius(other.m_radius) - , m_snap_coarse_in_radius(other.m_snap_coarse_in_radius) - , m_snap_coarse_out_radius(other.m_snap_coarse_out_radius) - , m_snap_fine_in_radius(other.m_snap_fine_in_radius) - , m_snap_fine_out_radius(other.m_snap_fine_out_radius) +void GLGizmoRotate::set_highlight_color(const ColorRGBA &color) { + m_highlight_color = color; } - void GLGizmoRotate::set_angle(double angle) { - if (std::abs(angle - 2.0 * (double)PI) < EPSILON) + if (std::abs(angle - 2.0 * double(PI)) < EPSILON) angle = 0.0; m_angle = angle; @@ -71,12 +60,35 @@ std::string GLGizmoRotate::get_tooltip() const case Y: { axis = "Y"; break; } case Z: { axis = "Z"; break; } } - return (m_hover_id == 0 || m_grabbers[0].dragging) ? axis + ": " + format((float)Geometry::rad2deg(m_angle), 2) : ""; + return (m_hover_id == 0 || m_grabbers.front().dragging) ? axis + ": " + format(float(Geometry::rad2deg(m_angle)), 2) : ""; } +bool GLGizmoRotate::on_mouse(const wxMouseEvent &mouse_event) +{ + return use_grabbers(mouse_event); +} + +void GLGizmoRotate::dragging(const UpdateData &data) { on_dragging(data); } + +void GLGizmoRotate::start_dragging() +{ + m_grabbers[0].dragging = true; + on_start_dragging(); +} + +void GLGizmoRotate::stop_dragging() +{ + m_grabbers[0].dragging = false; + on_stop_dragging(); +} + +void GLGizmoRotate::enable_grabber() { m_grabbers[0].enabled = true; } +void GLGizmoRotate::disable_grabber() { m_grabbers[0].enabled = false; } + bool GLGizmoRotate::on_init() { m_grabbers.push_back(Grabber()); + m_grabbers.back().extensions = (GLGizmoBase::EGrabberExtension)(int(GLGizmoBase::EGrabberExtension::PosY) | int(GLGizmoBase::EGrabberExtension::NegY)); return true; } @@ -91,36 +103,33 @@ void GLGizmoRotate::on_start_dragging() m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; } -void GLGizmoRotate::on_update(const UpdateData& data) +void GLGizmoRotate::on_dragging(const UpdateData &data) { - Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); - Vec2d orig_dir = Vec2d::UnitX(); - Vec2d new_dir = mouse_pos.normalized(); + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); if (cross2(orig_dir, new_dir) < 0.0) theta = 2.0 * (double)PI - theta; - double len = mouse_pos.norm(); + const double len = mouse_pos.norm(); // snap to coarse snap region - if ((m_snap_coarse_in_radius <= len) && (len <= m_snap_coarse_out_radius)) - { - double step = 2.0 * (double)PI / (double)SnapRegionsCount; - theta = step * (double)std::round(theta / step); + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = 2.0 * double(PI) / double(SnapRegionsCount); + theta = step * std::round(theta / step); } - else - { + else { // snap to fine snap region (scale) - if ((m_snap_fine_in_radius <= len) && (len <= m_snap_fine_out_radius)) - { - double step = 2.0 * (double)PI / (double)ScaleStepsCount; - theta = step * (double)std::round(theta / step); + if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = 2.0 * double(PI) / double(ScaleStepsCount); + theta = step * std::round(theta / step); } } - if (theta == 2.0 * (double)PI) + if (theta == 2.0 * double(PI)) theta = 0.0; m_angle = theta; @@ -128,13 +137,13 @@ void GLGizmoRotate::on_update(const UpdateData& data) void GLGizmoRotate::on_render() { - if (!m_grabbers[0].enabled) + if (!m_grabbers.front().enabled) return; const Selection& selection = m_parent.get_selection(); const BoundingBoxf3& box = selection.get_bounding_box(); - if (m_hover_id != 0 && !m_grabbers[0].dragging) { + if (m_hover_id != 0 && !m_grabbers.front().dragging) { m_center = m_custom_center == Vec3d::Zero() ? box.center() : m_custom_center; m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; @@ -143,48 +152,47 @@ void GLGizmoRotate::on_render() m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth); } + const double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); + m_grabbers.front().center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); + m_grabbers.front().angles.z() = m_angle; + m_grabbers.front().color = AXES_COLOR[m_axis]; + m_grabbers.front().hover_color = AXES_HOVER_COLOR[m_axis]; + glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - transform_to_local(selection); + m_grabbers.front().matrix = local_transform(selection); glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); - glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); - render_circle(); + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * m_grabbers.front().matrix; - if (m_hover_id != -1) { - render_scale(); - render_snap_radii(); - render_reference_radius(); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON; + m_old_radius = m_radius; + + ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color); + render_circle(color, radius_changed); + if (m_hover_id != -1) { + const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON; + m_old_hover_radius = m_radius; + + render_scale(color, hover_radius_changed); + render_snap_radii(color, hover_radius_changed); + render_reference_radius(color, hover_radius_changed); + render_angle_arc(m_highlight_color, hover_radius_changed); + } + + render_grabber_connection(color, radius_changed); + shader->stop_using(); } - glsafe(::glColor4fv(m_highlight_color.data())); - - if (m_hover_id != -1) - render_angle(); - render_grabber(box); - render_grabber_extension(box, false); - - glsafe(::glPopMatrix()); -} - -void GLGizmoRotate::on_render_for_picking() -{ - const Selection& selection = m_parent.get_selection(); - - glsafe(::glDisable(GL_DEPTH_TEST)); - - glsafe(::glPushMatrix()); - - transform_to_local(selection); - - const BoundingBoxf3& box = selection.get_bounding_box(); - render_grabbers_for_picking(box); - render_grabber_extension(box, true); - - glsafe(::glPopMatrix()); } //BBS: add input window for move @@ -219,192 +227,226 @@ void GLGizmoRotate3D::load_rotoptimize_state() } } -void GLGizmoRotate::render_circle() const +void GLGizmoRotate::render_circle(const ColorRGBA& color, bool radius_changed) { - ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float x = ::cos(angle) * m_radius; - float y = ::sin(angle) * m_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + if (!m_circle.is_initialized() || radius_changed) { + m_circle.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(ScaleStepsCount); + init_data.reserve_indices(ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + init_data.add_vertex(Vec3f(::cos(angle) * m_radius, ::sin(angle) * m_radius, 0.0f)); + init_data.add_index(i); + } + + m_circle.init_from(std::move(init_data)); } - glsafe(::glEnd()); + + m_circle.set_color(color); + m_circle.render(); } -void GLGizmoRotate::render_scale() const +void GLGizmoRotate::render_scale(const ColorRGBA& color, bool radius_changed) { - float out_radius_long = m_snap_fine_out_radius; - float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); + const float out_radius_long = m_snap_fine_out_radius; + const float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * m_radius; - float in_y = sina * m_radius; - float in_z = 0.0f; - float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; - float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; - float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + if (!m_scale.is_initialized() || radius_changed) { + m_scale.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * m_radius; + const float in_y = sina * m_radius; + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + m_scale.init_from(std::move(init_data)); } - glsafe(::glEnd()); + + m_scale.set_color(color); + m_scale.render(); } -void GLGizmoRotate::render_snap_radii() const +void GLGizmoRotate::render_snap_radii(const ColorRGBA& color, bool radius_changed) { - float step = 2.0f * (float)PI / (float)SnapRegionsCount; + const float step = 2.0f * float(PI) / float(SnapRegionsCount); + const float in_radius = m_radius / 3.0f; + const float out_radius = 2.0f * in_radius; - float in_radius = m_radius / 3.0f; - float out_radius = 2.0f * in_radius; + if (!m_snap_radii.is_initialized() || radius_changed) { + m_snap_radii.reset(); - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < SnapRegionsCount; ++i) - { - float angle = (float)i * step; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * in_radius; - float in_y = sina * in_radius; - float in_z = 0.0f; - float out_x = cosa * out_radius; - float out_y = sina * out_radius; - float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * step); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + m_snap_radii.init_from(std::move(init_data)); } - glsafe(::glEnd()); + + m_snap_radii.set_color(color); + m_snap_radii.render(); } -void GLGizmoRotate::render_reference_radius() const +void GLGizmoRotate::render_reference_radius(const ColorRGBA& color, bool radius_changed) { - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)(m_radius * (1.0f + GrabberOffset)), 0.0f, 0.0f); - glsafe(::glEnd()); -} + if (!m_reference_radius.is_initialized() || radius_changed) { + m_reference_radius.reset(); -void GLGizmoRotate::render_angle() const -{ - float step_angle = (float)m_angle / AngleResolution; - float ex_radius = m_radius * (1.0f + GrabberOffset); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i <= AngleResolution; ++i) - { - float angle = (float)i * step_angle; - float x = ::cos(angle) * ex_radius; - float y = ::sin(angle) * ex_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(m_radius * (1.0f + GrabberOffset), 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_reference_radius.init_from(std::move(init_data)); } - glsafe(::glEnd()); + + m_reference_radius.set_color(color); + m_reference_radius.render(); } -void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const +void GLGizmoRotate::render_angle_arc(const ColorRGBA& color, bool radius_changed) { - double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); - m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); - m_grabbers[0].angles(2) = m_angle; - m_grabbers[0].color = AXES_COLOR[m_axis]; - m_grabbers[0].hover_color = AXES_HOVER_COLOR[m_axis]; + const float step_angle = float(m_angle) / float(AngleResolution); + const float ex_radius = m_radius * (1.0f + GrabberOffset); - glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); + const bool angle_changed = std::abs(m_old_angle - m_angle) > EPSILON; + m_old_angle = m_angle; - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3dv(m_grabbers[0].center.data()); - glsafe(::glEnd()); + if (!m_angle_arc.is_initialized() || radius_changed || angle_changed) { + m_angle_arc.reset(); + if (m_angle > 0.0f) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(1 + AngleResolution); + init_data.reserve_indices(1 + AngleResolution); - m_grabbers[0].color = m_highlight_color; + // vertices + indices + for (unsigned int i = 0; i <= AngleResolution; ++i) { + const float angle = float(i) * step_angle; + init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f)); + init_data.add_index(i); + } + + m_angle_arc.init_from(std::move(init_data)); + } + } + + m_angle_arc.set_color(color); + m_angle_arc.render(); +} + +void GLGizmoRotate::render_grabber_connection(const ColorRGBA& color, bool radius_changed) +{ + if (!m_grabber_connection.model.is_initialized() || radius_changed || !m_grabber_connection.old_center.isApprox(m_grabbers.front().center)) { + m_grabber_connection.model.reset(); + m_grabber_connection.old_center = m_grabbers.front().center; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex((Vec3f)m_grabbers.front().center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connection.model.init_from(std::move(init_data)); + } + + m_grabber_connection.model.set_color(color); + m_grabber_connection.model.render(); +} + +void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) +{ + m_grabbers.front().color = m_highlight_color; render_grabbers(box); } -void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) const +Transform3d GLGizmoRotate::local_transform(const Selection& selection) const { - double size = 0.75 * GLGizmoBase::Grabber::FixedGrabberSize * GLGizmoBase::INV_ZOOM; - //float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); - //double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size); - - std::array color = m_grabbers[0].color; - if (!picking && m_hover_id != -1) { - color = m_grabbers[0].hover_color; - //color[0] = 1.0f - color[0]; - //color[1] = 1.0f - color[1]; - //color[2] = 1.0f - color[2]; - } - - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - const_cast(&m_cone)->set_color(-1, color); - if (!picking) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - } - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_grabbers[0].center.x(), m_grabbers[0].center.y(), m_grabbers[0].center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 1.5 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); - m_cone.render(); - glsafe(::glPopMatrix()); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_grabbers[0].center.x(), m_grabbers[0].center.y(), m_grabbers[0].center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - glsafe(::glTranslated(0.0, 0.0, 1.5 * size)); - glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); - m_cone.render(); - glsafe(::glPopMatrix()); - - if (! picking) - shader->stop_using(); -} - -void GLGizmoRotate::transform_to_local(const Selection& selection) const -{ - glsafe(::glTranslated(m_center(0), m_center(1), m_center(2))); - - if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { - Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); - glsafe(::glMultMatrixd(orient_matrix.data())); - } + Transform3d ret; switch (m_axis) { case X: { - glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f)); - glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); + ret = Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY()) * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ()); break; } case Y: { - glsafe(::glRotatef(-90.0f, 0.0f, 0.0f, 1.0f)); - glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f)); + ret = Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ()) * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitY()); break; } default: case Z: { - // no rotation + ret = Transform3d::Identity(); break; } } + + if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) + ret = selection.get_first_volume()->get_instance_transformation().get_matrix(true, false, true, true) * ret; + + return Geometry::assemble_transform(m_center) * ret; } Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const { - double half_pi = 0.5 * (double)PI; + const double half_pi = 0.5 * double(PI); Transform3d m = Transform3d::Identity(); @@ -431,40 +473,75 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons } if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) - m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse(); + m = m * selection.get_first_volume()->get_instance_transformation().get_matrix(true, false, true, true).inverse(); m.translate(-m_center); - return transform(mouse_ray, m).intersect_plane(0.0); + const Linef3 local_mouse_ray = transform(mouse_ray, m); + if (std::abs(local_mouse_ray.vector().dot(Vec3d::UnitZ())) < EPSILON) { + // if the ray is parallel to the plane containing the circle + if (std::abs(local_mouse_ray.vector().dot(Vec3d::UnitY())) > 1.0 - EPSILON) + // if the ray is parallel to grabber direction + return Vec3d::UnitX(); + else { + const Vec3d world_pos = (local_mouse_ray.a.x() >= 0.0) ? mouse_ray.a - m_center : mouse_ray.b - m_center; + m.translate(m_center); + return m * world_pos; + } + } + else + return local_mouse_ray.intersect_plane(0.0); } //BBS: GUI refactor: add obj manipulation GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) : GLGizmoBase(parent, icon_filename, sprite_id) + , m_gizmos({ GLGizmoRotate(parent, GLGizmoRotate::X), GLGizmoRotate(parent, GLGizmoRotate::Y), GLGizmoRotate(parent, GLGizmoRotate::Z) }) //BBS: GUI refactor: add obj manipulation , m_object_manipulation(obj_manipulation) { - m_gizmos.emplace_back(parent, GLGizmoRotate::X); - m_gizmos.emplace_back(parent, GLGizmoRotate::Y); - m_gizmos.emplace_back(parent, GLGizmoRotate::Z); - - for (unsigned int i = 0; i < 3; ++i) { - m_gizmos[i].set_group_id(i); - } - load_rotoptimize_state(); } +bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) { + + if (mouse_event.Dragging() && m_dragging) { + // Apply new temporary rotations + TransformationType transformation_type( + TransformationType::World_Relative_Joint); + if (mouse_event.AltDown()) + transformation_type.set_independent(); + m_parent.get_selection().rotate(get_rotation(), transformation_type); + } + return use_grabbers(mouse_event); +} + +void GLGizmoRotate3D::data_changed(bool is_serializing) { + const Selection &selection = m_parent.get_selection(); + bool is_wipe_tower = selection.is_wipe_tower(); + if (is_wipe_tower) { + DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + float wipe_tower_rotation_angle = + dynamic_cast( + config.option("wipe_tower_rotation_angle")) + ->value; + set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle)); + m_gizmos[0].disable_grabber(); + m_gizmos[1].disable_grabber(); + } else { + set_rotation(Vec3d::Zero()); + m_gizmos[0].enable_grabber(); + m_gizmos[1].enable_grabber(); + } +} + bool GLGizmoRotate3D::on_init() { - for (GLGizmoRotate& g : m_gizmos) { - if (!g.init()) - return false; - } + for (GLGizmoRotate& g : m_gizmos) + if (!g.init()) return false; - for (unsigned int i = 0; i < 3; ++i) { + for (unsigned int i = 0; i < 3; ++i) m_gizmos[i].set_highlight_color(AXES_COLOR[i]); - } m_shortcut_key = WXK_CONTROL_R; @@ -485,14 +562,21 @@ bool GLGizmoRotate3D::on_is_activable() const void GLGizmoRotate3D::on_start_dragging() { - if ((0 <= m_hover_id) && (m_hover_id < 3)) - m_gizmos[m_hover_id].start_dragging(); + assert(0 <= m_hover_id && m_hover_id < 3); + m_gizmos[m_hover_id].start_dragging(); } void GLGizmoRotate3D::on_stop_dragging() { - if ((0 <= m_hover_id) && (m_hover_id < 3)) - m_gizmos[m_hover_id].stop_dragging(); + assert(0 <= m_hover_id && m_hover_id < 3); + m_parent.do_rotate(L("Gizmo-Rotate")); + m_gizmos[m_hover_id].stop_dragging(); +} + +void GLGizmoRotate3D::on_dragging(const UpdateData &data) +{ + assert(0 <= m_hover_id && m_hover_id < 3); + m_gizmos[m_hover_id].dragging(data); } void GLGizmoRotate3D::on_render() @@ -509,6 +593,23 @@ void GLGizmoRotate3D::on_render() m_gizmos[Z].render(); } +void GLGizmoRotate3D::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); + for (GLGizmoRotate& g : m_gizmos) { + g.register_raycasters_for_picking(); + } +} + +void GLGizmoRotate3D::on_unregister_raycasters_for_picking() +{ + for (GLGizmoRotate& g : m_gizmos) { + g.unregister_raycasters_for_picking(); + } + m_parent.set_raycaster_gizmos_on_top(false); +} + GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, State & state, const Alignment &alignment) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 9862ec2901..edafc5ae23 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Tomáš Mészáros @tamasmeszaros +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoRotate_hpp_ #define slic3r_GLGizmoRotate_hpp_ @@ -9,11 +13,10 @@ namespace Slic3r { namespace GUI { - +class Selection; class GLGizmoRotate : public GLGizmoBase { static const float Offset; - static const unsigned int CircleResolution; static const unsigned int AngleResolution; static const unsigned int ScaleStepsCount; static const float ScaleStepRad; @@ -25,27 +28,42 @@ class GLGizmoRotate : public GLGizmoBase public: enum Axis : unsigned char { - X, - Y, - Z + X=0, + Y=1, + Z=2 }; private: Axis m_axis; - double m_angle; + double m_angle{ 0.0 }; + Vec3d m_custom_center{Vec3d::Zero()}; + Vec3d m_center{ Vec3d::Zero() }; + float m_radius{ 0.0f }; + float m_snap_coarse_in_radius{ 0.0f }; + float m_snap_coarse_out_radius{ 0.0f }; + float m_snap_fine_in_radius{ 0.0f }; + float m_snap_fine_out_radius{ 0.0f }; + + ColorRGBA m_drag_color; + ColorRGBA m_highlight_color; - mutable Vec3d m_custom_center{Vec3d::Zero()}; - mutable Vec3d m_center; - mutable float m_radius; - - mutable float m_snap_coarse_in_radius; - mutable float m_snap_coarse_out_radius; - mutable float m_snap_fine_in_radius; - mutable float m_snap_fine_out_radius; + GLModel m_circle; + GLModel m_scale; + GLModel m_snap_radii; + GLModel m_reference_radius; + GLModel m_angle_arc; + struct GrabberConnection + { + GLModel model; + Vec3d old_center{ Vec3d::Zero() }; + }; + GrabberConnection m_grabber_connection; + float m_old_radius{ 0.0f }; + float m_old_hover_radius{ 0.0f }; + float m_old_angle{ 0.0f }; public: GLGizmoRotate(GLCanvas3D& parent, Axis axis); - GLGizmoRotate(const GLGizmoRotate& other); virtual ~GLGizmoRotate() = default; double get_angle() const { return m_angle; } @@ -55,24 +73,41 @@ public: void set_center(const Vec3d &point) { m_custom_center = point; } + void start_dragging(); + void stop_dragging(); + + void enable_grabber(); + void disable_grabber(); + + void set_highlight_color(const ColorRGBA &color); + + /// + /// Postpone to Grabber for move + /// Detect move of object by dragging + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + void dragging(const UpdateData &data); + protected: bool on_init() override; std::string on_get_name() const override { return ""; } void on_start_dragging() override; - void on_update(const UpdateData& data) override; + void on_dragging(const UpdateData &data) override; void on_render() override; - void on_render_for_picking() override; private: - void render_circle() const; - void render_scale() const; - void render_snap_radii() const; - void render_reference_radius() const; - void render_angle() const; - void render_grabber(const BoundingBoxf3& box) const; - void render_grabber_extension(const BoundingBoxf3& box, bool picking) const; + void render_circle(const ColorRGBA& color, bool radius_changed); + void render_scale(const ColorRGBA& color, bool radius_changed); + void render_snap_radii(const ColorRGBA& color, bool radius_changed); + void render_reference_radius(const ColorRGBA& color, bool radius_changed); + void render_angle_arc(const ColorRGBA& color, bool radius_changed); + void render_grabber_connection(const ColorRGBA& color, bool radius_changed); + void render_grabber(const BoundingBoxf3& box); + + Transform3d local_transform(const Selection& selection) const; - void transform_to_local(const Selection& selection) const; // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const; }; @@ -81,7 +116,7 @@ class GLGizmoRotate3D : public GLGizmoBase { // BBS: change to protected for subclass access protected: - std::vector m_gizmos; + std::array m_gizmos; //BBS: add size adjust related GizmoObjectManipulation* m_object_manipulation; @@ -92,10 +127,9 @@ public: GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } - void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } + void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation.x()); m_gizmos[Y].set_angle(rotation.y()); m_gizmos[Z].set_angle(rotation.z()); } - std::string get_tooltip() const override - { + std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); if (tooltip.empty()) tooltip = m_gizmos[Y].get_tooltip(); @@ -111,54 +145,44 @@ public: m_gizmos[Z].set_center(point); } + /// + /// Postpone to Rotation + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed(bool is_serializing) override; protected: bool on_init() override; std::string on_get_name() const override; - void on_set_state() override - { + void on_set_state() override { for (GLGizmoRotate& g : m_gizmos) g.set_state(m_state); } - void on_set_hover_id() override - { + void on_set_hover_id() override { for (int i = 0; i < 3; ++i) m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } - void on_enable_grabber(unsigned int id) override - { - if (id < 3) - m_gizmos[id].enable_grabber(0); - } - void on_disable_grabber(unsigned int id) override - { - if (id < 3) - m_gizmos[id].disable_grabber(0); - } + bool on_is_activable() const override; void on_start_dragging() override; void on_stop_dragging() override; - void on_update(const UpdateData& data) override - { - for (GLGizmoRotate& g : m_gizmos) { - g.update(data); - } - } + void on_dragging(const UpdateData &data) override; + void on_render() override; - void on_render_for_picking() override - { - for (GLGizmoRotate& g : m_gizmos) { - g.render_for_picking(); - } - } + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; void on_render_input_window(float x, float y, float bottom_limit) override; private: - class RotoptimzeWindow { + class RotoptimzeWindow + { ImGuiWrapper *m_imgui = nullptr; - public: + public: struct State { float accuracy = 1.f; int method_id = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 0f98c5dd35..93fbf7d9b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -1,7 +1,11 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoScale.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" #include @@ -24,12 +28,19 @@ Vec3d GetIntersectionOfRayAndPlane(Vec3d ray_position, Vec3d ray_dir, Vec3d plan //BBS: GUI refactor: add obj manipulation GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_scale(Vec3d::Ones()) - , m_offset(Vec3d::Zero()) - , m_snap_step(0.05) //BBS: GUI refactor: add obj manipulation , m_object_manipulation(obj_manipulation) + , m_base_color(DEFAULT_BASE_COLOR) + , m_drag_color(DEFAULT_DRAG_COLOR) + , m_highlight_color(DEFAULT_HIGHLIGHT_COLOR) { + m_grabber_connections[0].grabber_indices = { 0, 1 }; + m_grabber_connections[1].grabber_indices = { 2, 3 }; + m_grabber_connections[2].grabber_indices = { 4, 5 }; + m_grabber_connections[3].grabber_indices = { 6, 7 }; + m_grabber_connections[4].grabber_indices = { 7, 8 }; + m_grabber_connections[5].grabber_indices = { 8, 9 }; + m_grabber_connections[6].grabber_indices = { 9, 6 }; } std::string GLGizmoScale3D::get_tooltip() const @@ -41,22 +52,22 @@ std::string GLGizmoScale3D::get_tooltip() const Vec3f scale = 100.0f * Vec3f::Ones(); if (single_instance) - scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast(); + scale = 100.0f * selection.get_first_volume()->get_instance_scaling_factor().cast(); else if (single_volume) - scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast(); + scale = 100.0f * selection.get_first_volume()->get_volume_scaling_factor().cast(); if (m_hover_id == 0 || m_hover_id == 1 || m_grabbers[0].dragging || m_grabbers[1].dragging) - return "X: " + format(scale(0), 4) + "%"; + return "X: " + format(scale.x(), 4) + "%"; else if (m_hover_id == 2 || m_hover_id == 3 || m_grabbers[2].dragging || m_grabbers[3].dragging) - return "Y: " + format(scale(1), 4) + "%"; + return "Y: " + format(scale.y(), 4) + "%"; else if (m_hover_id == 4 || m_hover_id == 5 || m_grabbers[4].dragging || m_grabbers[5].dragging) - return "Z: " + format(scale(2), 4) + "%"; + return "Z: " + format(scale.z(), 4) + "%"; else if (m_hover_id == 6 || m_hover_id == 7 || m_hover_id == 8 || m_hover_id == 9 || m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging) { - std::string tooltip = "X: " + format(scale(0), 2) + "%\n"; - tooltip += "Y: " + format(scale(1), 2) + "%\n"; - tooltip += "Z: " + format(scale(2), 2) + "%"; + std::string tooltip = "X: " + format(scale.x(), 2) + "%\n"; + tooltip += "Y: " + format(scale.y(), 2) + "%\n"; + tooltip += "Z: " + format(scale.z(), 2) + "%"; return tooltip; } else @@ -69,10 +80,49 @@ void GLGizmoScale3D::enable_ununiversal_scale(bool enable) m_grabbers[i].enabled = enable; } +bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Dragging()) { + if (m_dragging) { + // Apply new temporary scale factors + TransformationType transformation_type(TransformationType::Local_Absolute_Joint); + if (mouse_event.AltDown()) + transformation_type.set_independent(); + + Selection& selection = m_parent.get_selection(); + selection.scale(m_scale, transformation_type); + if (mouse_event.CmdDown()) selection.translate(m_offset, true); + } + } + return use_grabbers(mouse_event); +} + +void GLGizmoScale3D::data_changed(bool is_serializing) { + const Selection &selection = m_parent.get_selection(); + bool enable_scale_xyz = selection.is_single_full_instance() || + selection.is_single_volume() || + selection.is_single_modifier(); + for (unsigned int i = 0; i < 6; ++i) + m_grabbers[i].enabled = enable_scale_xyz; + + if (enable_scale_xyz) { + // all volumes in the selection belongs to the same instance, any of + // them contains the needed data, so we take the first + const GLVolume *volume = selection.get_first_volume(); + if (selection.is_single_full_instance()) { + set_scale(volume->get_instance_scaling_factor()); + } else if (selection.is_single_volume() || + selection.is_single_modifier()) { + set_scale(volume->get_volume_scaling_factor()); + } + } else { + set_scale(Vec3d::Ones()); + } +} + bool GLGizmoScale3D::on_init() { - for (int i = 0; i < 10; ++i) - { + for (int i = 0; i < 10; ++i) { m_grabbers.push_back(Grabber()); } @@ -106,31 +156,33 @@ bool GLGizmoScale3D::on_is_activable() const void GLGizmoScale3D::on_start_dragging() { - if (m_hover_id != -1) - { - m_starting.drag_position = m_grabbers[m_hover_id].center; - m_starting.plane_center = m_grabbers[4].center; - m_starting.plane_nromal = m_grabbers[5].center - m_grabbers[4].center; - m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); - m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box(); + assert(m_hover_id != -1); + m_starting.drag_position = m_grabbers[m_hover_id].center; + m_starting.plane_center = m_grabbers[4].center; + m_starting.plane_nromal = m_grabbers[5].center - m_grabbers[4].center; + m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); + m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box(); - const Vec3d& center = m_starting.box.center(); - m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2)); - m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min(0), center(1), center(2)); - m_starting.pivots[2] = m_transform * Vec3d(center(0), m_starting.box.max(1), center(2)); - m_starting.pivots[3] = m_transform * Vec3d(center(0), m_starting.box.min(1), center(2)); - m_starting.pivots[4] = m_transform * Vec3d(center(0), center(1), m_starting.box.max(2)); - m_starting.pivots[5] = m_transform * Vec3d(center(0), center(1), m_starting.box.min(2)); - } + const Vec3d& center = m_starting.box.center(); + m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z()); + m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min.x(), center.y(), center.z()); + m_starting.pivots[2] = m_transform * Vec3d(center.x(), m_starting.box.max.y(), center.z()); + m_starting.pivots[3] = m_transform * Vec3d(center.x(), m_starting.box.min.y(), center.z()); + m_starting.pivots[4] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.max.z()); + m_starting.pivots[5] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.min.z()); } -void GLGizmoScale3D::on_update(const UpdateData& data) +void GLGizmoScale3D::on_stop_dragging() { + m_parent.do_scale(L("Gizmo-Scale")); +} + +void GLGizmoScale3D::on_dragging(const UpdateData& data) { - if ((m_hover_id == 0) || (m_hover_id == 1)) + if (m_hover_id == 0 || m_hover_id == 1) do_scale_along_axis(X, data); - else if ((m_hover_id == 2) || (m_hover_id == 3)) + else if (m_hover_id == 2 || m_hover_id == 3) do_scale_along_axis(Y, data); - else if ((m_hover_id == 4) || (m_hover_id == 5)) + else if (m_hover_id == 4 || m_hover_id == 5) do_scale_along_axis(Z, data); else if (m_hover_id >= 6) do_scale_uniform(data); @@ -162,7 +214,7 @@ void GLGizmoScale3D::on_render() } // gets transform from first selected volume - const GLVolume* v = selection.get_volume(*idxs.begin()); + const GLVolume* v = selection.get_first_volume(); m_transform = v->get_instance_transformation().get_matrix(); // gets angles from first selected volume angles = v->get_instance_rotation(); @@ -171,7 +223,7 @@ void GLGizmoScale3D::on_render() m_offsets_transform = offsets_transform; } else if (single_volume) { - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* v = selection.get_first_volume(); m_box = v->bounding_box(); m_transform = v->world_matrix(); angles = Geometry::extract_euler_angles(m_transform); @@ -183,31 +235,31 @@ void GLGizmoScale3D::on_render() m_box = selection.get_bounding_box(); const Vec3d& center = m_box.center(); - Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0); - Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0); - Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset); + const Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0); + const Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0); + const Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset); - bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); + const bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); // x axis - m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), m_box.min(2)); - m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), m_box.min(2)); + m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), m_box.min.z()); + m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), m_box.min.z()); // y axis - m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), m_box.min(2)); - m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), m_box.min(2)); + m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), m_box.min.z()); + m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), m_box.min.z()); // z axis do not show 4 - m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)); + m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()); m_grabbers[4].enabled = false; - m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)); + m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()); // uniform - m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), m_box.min(2)); - m_grabbers[7].center = m_transform * Vec3d(m_box.max(0), m_box.min(1), m_box.min(2)); - m_grabbers[8].center = m_transform * Vec3d(m_box.max(0), m_box.max(1), m_box.min(2)); - m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), m_box.min(2)); + m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), m_box.min.z()); + m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), m_box.min.z()); + m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), m_box.min.z()); + m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), m_box.min.z()); for (int i = 0; i < 6; ++i) { m_grabbers[i].color = AXES_COLOR[i/2]; @@ -228,51 +280,90 @@ void GLGizmoScale3D::on_render() const BoundingBoxf3& selection_box = selection.get_bounding_box(); - float grabber_mean_size = (float)((selection_box.size()(0) + selection_box.size()(1) + selection_box.size()(2)) / 3.0); + const float grabber_mean_size = (float)((selection_box.size().x() + selection_box.size().y() + selection_box.size().z()) / 3.0); - //draw connections - - // BBS: when select multiple objects, uniform scale can be deselected, display the connection(4,5) - //if (single_instance || single_volume) { - - if (m_grabbers[4].enabled && m_grabbers[5].enabled) { - glsafe(::glColor4fv(m_grabbers[4].color.data())); - render_grabbers_connection(4, 5); + //draw connections + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + // BBS: when select multiple objects, uniform scale can be deselected, display the connection(4,5) + //if (single_instance || single_volume) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (m_grabbers[4].enabled && m_grabbers[5].enabled) + render_grabbers_connection(4, 5, m_grabbers[4].color); + render_grabbers_connection(6, 7, m_grabbers[2].color); + render_grabbers_connection(7, 8, m_grabbers[2].color); + render_grabbers_connection(8, 9, m_grabbers[0].color); + render_grabbers_connection(9, 6, m_grabbers[0].color); + shader->stop_using(); } - glsafe(::glColor4fv(m_grabbers[2].color.data())); - render_grabbers_connection(6, 7); - render_grabbers_connection(8, 9); - - glsafe(::glColor4fv(m_grabbers[0].color.data())); - render_grabbers_connection(7, 8); - render_grabbers_connection(9, 6); - // draw grabbers - render_grabbers(grabber_mean_size); -} - -void GLGizmoScale3D::on_render_for_picking() -{ - glsafe(::glDisable(GL_DEPTH_TEST)); - render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); -} - -void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const -{ - unsigned int grabbers_count = (unsigned int)m_grabbers.size(); - if ((id_1 < grabbers_count) && (id_2 < grabbers_count)) - { - glLineStipple(1, 0x0FFF); - glEnable(GL_LINE_STIPPLE); - ::glBegin(GL_LINES); - ::glVertex3dv(m_grabbers[id_1].center.data()); - ::glVertex3dv(m_grabbers[id_2].center.data()); - glsafe(::glEnd()); - glDisable(GL_LINE_STIPPLE); + shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + render_grabbers(grabber_mean_size); + shader->stop_using(); } } +void GLGizmoScale3D::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoScale3D::on_unregister_raycasters_for_picking() +{ + m_parent.set_raycaster_gizmos_on_top(false); +} + +void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color) +{ + auto grabber_connection = [this](unsigned int id_1, unsigned int id_2) { + for (int i = 0; i < int(m_grabber_connections.size()); ++i) { + if (m_grabber_connections[i].grabber_indices.first == id_1 && m_grabber_connections[i].grabber_indices.second == id_2) + return i; + } + return -1; + }; + + const int id = grabber_connection(id_1, id_2); + if (id == -1) + return; + + if (!m_grabber_connections[id].model.is_initialized() || + !m_grabber_connections[id].old_v1.isApprox(m_grabbers[id_1].center) || + !m_grabber_connections[id].old_v2.isApprox(m_grabbers[id_2].center)) { + m_grabber_connections[id].old_v1 = m_grabbers[id_1].center; + m_grabber_connections[id].old_v2 = m_grabbers[id_2].center; + m_grabber_connections[id].model.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)m_grabbers[id_1].center.cast()); + init_data.add_vertex((Vec3f)m_grabbers[id_2].center.cast()); + + // indices + init_data.add_line(0, 1); + + m_grabber_connections[id].model.init_from(std::move(init_data)); + } + + m_grabber_connections[id].model.set_color(color); + glLineStipple(1, 0x0FFF); + glEnable(GL_LINE_STIPPLE); + m_grabber_connections[id].model.render(); + glDisable(GL_LINE_STIPPLE); +} + //BBS: add input window for move void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit) { @@ -283,11 +374,9 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) { double ratio = calc_ratio(data); - if (ratio > 0.0) - { + if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; - if (m_starting.ctrl_down) - { + if (m_starting.ctrl_down) { double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis); if (m_hover_id == 2 * axis) local_offset *= -1.0; @@ -308,11 +397,10 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) } } -void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) +void GLGizmoScale3D::do_scale_uniform(const UpdateData & data) { - double ratio = calc_ratio(data); - if (ratio > 0.0) - { + const double ratio = calc_ratio(data); + if (ratio > 0.0) { m_scale = m_starting.scale * ratio; m_offset = Vec3d::Zero(); } @@ -322,11 +410,11 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const { double ratio = 0.0; - Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.pivots[m_hover_id] : m_starting.plane_center; + Vec3d pivot = (m_starting.ctrl_down && m_hover_id < 6) ? m_starting.pivots[m_hover_id] : m_starting.plane_center; + Vec3d starting_vec = m_starting.drag_position - pivot; double len_starting_vec = starting_vec.norm(); - if (len_starting_vec != 0.0) - { + if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); Vec3d plane_normal = m_starting.plane_nromal; if (m_hover_id == 5) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 839c7f6823..589b074491 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoScale_hpp_ #define slic3r_GLGizmoScale_hpp_ @@ -28,15 +32,28 @@ class GLGizmoScale3D : public GLGizmoBase StartingData() : scale(Vec3d::Ones()), drag_position(Vec3d::Zero()), ctrl_down(false) { for (int i = 0; i < 5; ++i) { pivots[i] = Vec3d::Zero(); } } }; - mutable BoundingBoxf3 m_box; - mutable Transform3d m_transform; + BoundingBoxf3 m_box; + Transform3d m_transform; // Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes) - mutable Transform3d m_offsets_transform; - Vec3d m_scale; - Vec3d m_offset; - double m_snap_step; + Transform3d m_offsets_transform; + Vec3d m_scale{ Vec3d::Ones() }; + Vec3d m_offset{ Vec3d::Zero() }; + double m_snap_step{ 0.05 }; StartingData m_starting; + ColorRGBA m_base_color; + ColorRGBA m_drag_color; + ColorRGBA m_highlight_color; + + struct GrabberConnection + { + GLModel model; + std::pair grabber_indices; + Vec3d old_v1{ Vec3d::Zero() }; + Vec3d old_v2{ Vec3d::Zero() }; + }; + std::array m_grabber_connections; + //BBS: add size adjust related GizmoObjectManipulation* m_object_manipulation; @@ -51,24 +68,32 @@ public: const Vec3d& get_scale() const { return m_scale; } void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; } - const Vec3d& get_offset() const { return m_offset; } - std::string get_tooltip() const override; + /// + /// Postpone to Grabber for scale + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed(bool is_serializing) override; void enable_ununiversal_scale(bool enable); protected: virtual bool on_init() override; virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; virtual void on_start_dragging() override; - virtual void on_update(const UpdateData& data) override; + virtual void on_stop_dragging() override; + virtual void on_dragging(const UpdateData& data) override; virtual void on_render() override; - virtual void on_render_for_picking() override; + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; //BBS: GUI refactor: add object manipulation virtual void on_render_input_window(float x, float y, float bottom_limit); private: - void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; + void render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color); void do_scale_along_axis(Axis axis, const UpdateData& data); void do_scale_uniform(const UpdateData& data); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index d0fae7c033..09e999ec52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoSeam.hpp" #include "libslic3r/Model.hpp" @@ -62,7 +66,7 @@ std::string GLGizmoSeam::on_get_name() const -void GLGizmoSeam::render_painter_gizmo() const +void GLGizmoSeam::render_painter_gizmo() { const Selection& selection = m_parent.get_selection(); @@ -120,8 +124,12 @@ void GLGizmoSeam::render_triangles(const Selection& selection) const if (is_left_handed) glsafe(::glFrontFace(GL_CW)); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); float normal_z = -::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg)); Matrix3f normal_matrix = static_cast(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast()); @@ -129,11 +137,10 @@ void GLGizmoSeam::render_triangles(const Selection& selection) const shader->set_uniform("volume_world_matrix", trafo_matrix); shader->set_uniform("volume_mirrored", is_left_handed); shader->set_uniform("slope.actived", m_parent.is_using_slope()); - shader->set_uniform("slope.volume_world_normal_matrix", static_cast(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast())); + shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix); shader->set_uniform("slope.normal_z", normal_z); - m_triangle_selectors[mesh_id]->render(m_imgui); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); - glsafe(::glPopMatrix()); if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); } @@ -295,8 +302,8 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) } else { if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this]() { - m_c->object_clipper()->set_position(-1., false); + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -310,7 +317,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(drag_left_width); ImGui::PushItemWidth(1.5 * slider_icon_width); bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); - if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); } + if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); } ImGui::Separator(); m_imgui->bbl_checkbox(_L("Vertical"), m_vertical_only); @@ -388,7 +395,7 @@ void GLGizmoSeam::update_from_model_object(bool first_update) m_triangle_selectors.clear(); int volume_id = -1; - std::vector> ebt_colors; + std::vector ebt_colors; ebt_colors.push_back(GLVolume::NEUTRAL_COLOR); ebt_colors.push_back(TriangleSelectorGUI::enforcers_color); ebt_colors.push_back(TriangleSelectorGUI::blockers_color); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp index 2ba58d31ed..2e20f50183 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoSeam_hpp_ #define slic3r_GLGizmoSeam_hpp_ @@ -10,7 +14,7 @@ class GLGizmoSeam : public GLGizmoPainterBase public: GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - void render_painter_gizmo() const override; + void render_painter_gizmo() override; //BBS bool on_key_down_select_tool_type(int keyCode); @@ -32,9 +36,9 @@ protected: wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; - std::string get_gizmo_entering_text() const override { return "Entering Seam painting"; } - std::string get_gizmo_leaving_text() const override { return "Leaving Seam painting"; } - std::string get_action_snapshot_name() override { return "Paint-on seam editing"; } + std::string get_gizmo_entering_text() const override { return _u8L("Entering Seam painting"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Seam painting"); } + std::string get_action_snapshot_name() const override { return _u8L("Paint-on seam editing"); } static const constexpr float CursorRadiusMin = 0.05f; // cannot be zero const float get_cursor_radius_min() const override { return CursorRadiusMin; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 7b7cc2065d..ca1575bba3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmoSimplify.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" @@ -6,6 +10,7 @@ #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/format.hpp" +#include "slic3r/GUI/OpenGLManager.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/QuadricEdgeCollapse.hpp" @@ -622,7 +627,7 @@ void GLGizmoSimplify::init_model(const indexed_triangle_set& its) m_c->selection_info()->get_active_instance(), m_volume); if (const Selection&sel = m_parent.get_selection(); sel.get_volume_idxs().size() == 1) - m_glmodel.set_color(-1, sel.get_volume(*sel.get_volume_idxs().begin())->color); + m_glmodel.set_color(sel.get_volume(*sel.get_volume_idxs().begin())->color); m_triangle_count = its.indices.size(); } @@ -641,19 +646,28 @@ void GLGizmoSimplify::on_render() return; const Transform3d trafo_matrix = selected_volume->world_matrix(); - glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(trafo_matrix.data())); - - auto *gouraud_shader = wxGetApp().get_shader("gouraud_light"); + auto* gouraud_shader = wxGetApp().get_shader("gouraud_light"); glsafe(::glPushAttrib(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST)); gouraud_shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d view_model_matrix = view_matrix * trafo_matrix; + gouraud_shader->set_uniform("view_model_matrix", view_model_matrix); + gouraud_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + gouraud_shader->set_uniform("view_normal_matrix", view_normal_matrix); m_glmodel.render(); gouraud_shader->stop_using(); if (m_show_wireframe) { auto* contour_shader = wxGetApp().get_shader("mm_contour"); contour_shader->start_using(); + contour_shader->set_uniform("offset", OpenGLManager::get_gl_info().is_mesa() ? 0.0005 : 0.00001); + contour_shader->set_uniform("view_model_matrix", view_model_matrix); + contour_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const ColorRGBA color = m_glmodel.get_color(); + m_glmodel.set_color(ColorRGBA::WHITE()); glsafe(::glLineWidth(1.0f)); glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)); //ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); }); @@ -661,11 +675,11 @@ void GLGizmoSimplify::on_render() //glsafe(::glPolygonOffset(5.0, 5.0)); m_glmodel.render(); glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); + m_glmodel.set_color(color); contour_shader->stop_using(); } glsafe(::glPopAttrib()); - glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 39093e14d6..dedcea8971 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmoSimplify_hpp_ #define slic3r_GLGizmoSimplify_hpp_ @@ -37,7 +41,6 @@ protected: // must implement virtual bool on_init() override { return true;}; virtual void on_render() override; - virtual void on_render_for_picking() override{}; CommonGizmosDataID on_get_requirements() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 73ed826fc6..ef46c7d002 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -77,6 +77,13 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S void GLGizmoSlaSupports::on_render() { + if (!m_cone.is_initialized()) + m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); + if (!m_sphere.is_initialized()) + m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); + if (!m_cylinder.is_initialized()) + m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0)); + ModelObject* mo = m_c->selection_info()->model_object(); const Selection& selection = m_parent.get_selection(); @@ -101,46 +108,38 @@ void GLGizmoSlaSupports::on_render() glsafe(::glDisable(GL_BLEND)); } - -void GLGizmoSlaSupports::on_render_for_picking() +void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) { - const Selection& selection = m_parent.get_selection(); - //glsafe(::glEnable(GL_DEPTH_TEST)); - render_points(selection, true); -} + const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); -void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const -{ - size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); - - bool has_points = (cache_size != 0); - bool has_holes = (! m_c->hollowed_mesh()->get_hollowed_mesh() + const bool has_points = (cache_size != 0); + const bool has_holes = (! m_c->hollowed_mesh()->get_hollowed_mesh() && ! m_c->selection_info()->model_object()->sla_drain_holes.empty()); if (! has_points && ! has_holes) return; - GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); - if (shader != nullptr) - shader->start_using(); - ScopeGuard guard([shader]() { - if (shader != nullptr) - shader->stop_using(); - }); + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); - const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); - float z_shift = m_c->selection_info()->get_sla_shift(); + const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * vol->get_instance_transformation().get_matrix(); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, z_shift)); - glsafe(::glMultMatrixd(instance_matrix.data())); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); - std::array render_color; + shader->set_uniform("projection_matrix", projection_matrix); + + ColorRGBA render_color; for (size_t i = 0; i < cache_size; ++i) { const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; - const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; + const bool point_selected = m_editing_mode ? m_editing_cache[i].selected : false; if (is_mesh_point_clipped(support_point.pos.cast())) continue; @@ -167,15 +166,13 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } } - const_cast(&m_cone)->set_color(-1, render_color); - const_cast(&m_sphere)->set_color(-1, render_color); - if (shader && !picking) + m_cone.set_color(render_color); + m_sphere.set_color(render_color); + if (!picking) shader->set_uniform("emission_factor", 0.5f); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + const Transform3d support_matrix = Geometry::assemble_transform(support_point.pos.cast()) * instance_scaling_matrix_inverse; if (vol->is_left_handed()) glFrontFace(GL_CW); @@ -188,75 +185,63 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + const Eigen::AngleAxisd aa(q); const double cone_radius = 0.25; // mm const double cone_height = 0.75; - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, cone_height + support_point.head_front_radius * RenderPointScale)); - glsafe(::glPushMatrix()); - glsafe(::glRotated(180., 1., 0., 0.)); - glsafe(::glScaled(cone_radius, cone_radius, cone_height)); + const Transform3d model_matrix = instance_matrix * support_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform((cone_height + support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), + Vec3d(PI, 0.0, 0.0), Vec3d(cone_radius, cone_radius, cone_height)); + + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_cone.render(); - glsafe(::glPopMatrix()); - glsafe(::glTranslatef(0.f, 0.f, cone_height)); - glsafe(::glPopMatrix()); } - glsafe(::glPushMatrix()); - double radius = (double)support_point.head_front_radius * RenderPointScale; - glsafe(::glScaled(radius, radius, radius)); + const double radius = (double)support_point.head_front_radius * RenderPointScale; + const Transform3d model_matrix = instance_matrix * support_matrix * + Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), radius * Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_sphere.render(); - glsafe(::glPopMatrix()); if (vol->is_left_handed()) glFrontFace(GL_CCW); - - glsafe(::glPopMatrix()); } // Now render the drain holes: if (has_holes && ! picking) { - render_color[0] = 0.7f; - render_color[1] = 0.7f; - render_color[2] = 0.7f; - render_color[3] = 0.7f; - const_cast(&m_cylinder)->set_color(-1, render_color); - if (shader) - shader->set_uniform("emission_factor", 0.5f); + render_color = { 0.7f, 0.7f, 0.7f, 0.7f }; + m_cylinder.set_color(render_color); + shader->set_uniform("emission_factor", 0.5f); for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + const Transform3d hole_matrix = Geometry::assemble_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; if (vol->is_left_handed()) glFrontFace(GL_CW); // Matrices set, we can render the point mark now. - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d model_matrix = instance_matrix * hole_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform(-drain_hole.height * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); + + shader->set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_cylinder.render(); - glsafe(::glPopMatrix()); if (vol->is_left_handed()) glFrontFace(GL_CCW); - glsafe(::glPopMatrix()); } } - - glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 8dd76336a3..e4e7b8241f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -75,9 +75,8 @@ private: bool on_init() override; void on_update(const UpdateData& data) override; void on_render() override; - void on_render_for_picking() override; - void render_points(const Selection& selection, bool picking = false) const; + void render_points(const Selection& selection, bool picking = false); bool unsaved_changes() const; bool m_lock_unique_islands = false; @@ -91,6 +90,10 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo ObjectID m_old_mo_id; + GLModel m_cone; + GLModel m_cylinder; + GLModel m_sphere; + // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 8726d73146..0d8461395f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -145,6 +145,8 @@ bool GLGizmoText::on_init() m_scale = m_imgui->get_font_size(); m_shortcut_key = WXK_CONTROL_T; + m_grabbers.push_back(Grabber()); + reset_text_info(); m_desc["font"] = _L("Font"); @@ -161,7 +163,6 @@ bool GLGizmoText::on_init() m_desc["rotate_text_caption"] = _L("Shift + Mouse move up or dowm"); m_desc["rotate_text"] = _L("Rotate text"); - m_grabbers.push_back(Grabber()); return true; } @@ -263,13 +264,8 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit m_mouse_position = mouse_position; } else if (action == SLAGizmoEventType::LeftDown) { - if (!selection.is_empty() && get_hover_id() != -1) { - start_dragging(); - return true; - } - if (m_is_modify) - return true; + return false; Plater *plater = wxGetApp().plater(); if (!plater) @@ -298,7 +294,7 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { @@ -330,6 +326,40 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit return true; } +bool GLGizmoText::on_mouse(const wxMouseEvent &mouse_event) +{ + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + bool control_down = mouse_event.CmdDown(); + + if (mouse_event.Moving()) { + gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down); + } + + // when control is down we allow scene pan and rotation even when clicking + // over some object + bool grabber_contains_mouse = (get_hover_id() != -1); + + if (mouse_event.LeftDown()) { + if ((!control_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + // the gizmo got the event and took some action, there is no need + // to do anything more + return true; + } + + return use_grabbers(mouse_event); +} + +void GLGizmoText::on_register_raycasters_for_picking() +{ + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoText::on_unregister_raycasters_for_picking() { m_parent.set_raycaster_gizmos_on_top(false); } + void GLGizmoText::on_set_state() { if (m_state == EState::On) { @@ -456,9 +486,15 @@ void GLGizmoText::on_render() m_grabbers[0].center = m_mouse_position_world; m_grabbers[0].enabled = true; - std::array color = picking_color_component(0); - m_grabbers[0].color = color; - m_grabbers[0].render_for_picking(mean_size); + + GLShaderProgram *shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + render_grabbers(mean_size); + + shader->stop_using(); + } } delete_temp_preview_text_volume(); @@ -470,45 +506,7 @@ void GLGizmoText::on_render() plater->update(); } -void GLGizmoText::on_render_for_picking() -{ - glsafe(::glDisable(GL_DEPTH_TEST)); - - int obejct_idx, volume_idx; - ModelVolume *model_volume = get_selected_single_volume(obejct_idx, volume_idx); - if (model_volume && !model_volume->get_text_info().m_text.empty()) { - if (m_grabbers.size() == 1) { - ModelObject *mo = m_c->selection_info()->model_object(); - if (m_is_modify) { - const Selection &selection = m_parent.get_selection(); - mo = selection.get_model()->objects[m_object_idx]; - } - if (mo == nullptr) return; - - const Selection & selection = m_parent.get_selection(); - const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - - // Precalculate transformations of individual meshes. - std::vector trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { - trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); - } - } - - m_mouse_position_world = trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2)); - - float mean_size = (float) (GLGizmoBase::Grabber::FixedGrabberSize); - m_grabbers[0].center = m_mouse_position_world; - m_grabbers[0].enabled = true; - std::array color = picking_color_component(0); - m_grabbers[0].color = color; - m_grabbers[0].render_for_picking(mean_size); - } - } -} - -void GLGizmoText::on_update(const UpdateData &data) +void GLGizmoText::on_dragging(const UpdateData &data) { Vec2d mouse_pos = Vec2d(data.mouse_pos.x(), data.mouse_pos.y()); const ModelObject *mo = m_c->selection_info()->model_object(); @@ -540,7 +538,7 @@ void GLGizmoText::on_update(const UpdateData &data) if (mesh_id == m_volume_idx) continue; - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); + MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr()); if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), &facet)) { @@ -647,7 +645,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) const Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance() || selection.is_single_full_object()) { - const GLVolume * gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume * gl_volume = selection.get_first_volume(); int object_idx = gl_volume->object_idx(); if (object_idx != m_object_idx || (object_idx == m_object_idx && m_volume_idx != -1)) { m_object_idx = object_idx; @@ -912,7 +910,7 @@ ModelVolume *GLGizmoText::get_selected_single_volume(int &out_object_idx, int &o { if (m_parent.get_selection().is_single_volume() || m_parent.get_selection().is_single_modifier()) { const Selection &selection = m_parent.get_selection(); - const GLVolume * gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume * gl_volume = selection.get_first_volume(); out_object_idx = gl_volume->object_idx(); ModelObject *model_object = selection.get_model()->objects[out_object_idx]; out_volume_idx = gl_volume->volume_idx(); @@ -938,6 +936,7 @@ void GLGizmoText::reset_text_info() m_keep_horizontal = false; m_is_modify = false; + m_grabbers[0].enabled = false; } bool GLGizmoText::update_text_positions(const std::vector& texts) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp index 53b792364f..e187c72211 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp @@ -83,6 +83,8 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + bool on_mouse(const wxMouseEvent &mouse_event) override; + bool is_mesh_point_clipped(const Vec3d &point, const Transform3d &trafo) const; BoundingBoxf3 bounding_box() const; @@ -91,8 +93,7 @@ protected: virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_update(const UpdateData &data) override; + virtual void on_dragging(const UpdateData &data) override; void push_combo_style(const float scale); void pop_combo_style(); void push_button_style(bool pressed); @@ -100,6 +101,8 @@ protected: virtual void on_set_state() override; virtual CommonGizmosDataID on_get_requirements() const override; virtual void on_render_input_window(float x, float y, float bottom_limit); + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; void show_tooltip_information(float x, float y); diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index 9751c37232..36435919b7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2021 Lukáš Hejl @hejllukas, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GLGizmos_hpp_ #define slic3r_GLGizmos_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 4e29d10afb..709cd47257 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "GLGizmosCommon.hpp" #include @@ -23,10 +27,10 @@ CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas) using c = CommonGizmosDataID; m_data[c::SelectionInfo].reset( new SelectionInfo(this)); m_data[c::InstancesHider].reset( new InstancesHider(this)); - m_data[c::HollowedMesh].reset( new HollowedMesh(this)); +// m_data[c::HollowedMesh].reset( new HollowedMesh(this)); m_data[c::Raycaster].reset( new Raycaster(this)); m_data[c::ObjectClipper].reset( new ObjectClipper(this)); - m_data[c::SupportsClipper].reset( new SupportsClipper(this)); + // m_data[c::SupportsClipper].reset( new SupportsClipper(this)); } @@ -59,13 +63,6 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const return inst_hider->is_valid() ? inst_hider : nullptr; } -HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const -{ - HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); - assert(hol_mesh); - return hol_mesh->is_valid() ? hol_mesh : nullptr; -} - Raycaster* CommonGizmosDataPool::raycaster() const { Raycaster* rc = dynamic_cast(m_data.at(CommonGizmosDataID::Raycaster).get()); @@ -81,13 +78,6 @@ ObjectClipper* CommonGizmosDataPool::object_clipper() const return (oc && oc->is_valid()) ? oc : nullptr; } -SupportsClipper* CommonGizmosDataPool::supports_clipper() const -{ - SupportsClipper* sc = dynamic_cast(m_data.at(CommonGizmosDataID::SupportsClipper).get()); - assert(sc); - return sc->is_valid() ? sc : nullptr; -} - #ifndef NDEBUG // Check the required resources one by one and return true if all // dependencies are met. @@ -117,12 +107,13 @@ bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID required) const void SelectionInfo::on_update() { const Selection& selection = get_pool()->get_canvas()->get_selection(); + + m_model_object = nullptr; + if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } - else - m_model_object = nullptr; } void SelectionInfo::on_release() @@ -132,8 +123,7 @@ void SelectionInfo::on_release() int SelectionInfo::get_active_instance() const { - const Selection& selection = get_pool()->get_canvas()->get_selection(); - return selection.get_instance_idx(); + return get_pool()->get_canvas()->get_selection().get_instance_idx(); } @@ -154,7 +144,7 @@ void InstancesHider::on_update() if (mo && active_inst != -1) { canvas->toggle_model_objects_visibility(false); canvas->toggle_model_objects_visibility(true, mo, active_inst); - canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + canvas->toggle_sla_auxiliaries_visibility(false, mo, active_inst); canvas->set_use_clipping_planes(true); // Some objects may be sinking, do not show whatever is below the bed. canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), z_min)); @@ -170,7 +160,7 @@ void InstancesHider::on_update() for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min)); - m_clippers.back()->set_mesh(*mesh); + m_clippers.back()->set_mesh(mesh->its); } m_old_meshes = meshes; } @@ -187,13 +177,6 @@ void InstancesHider::on_release() m_clippers.clear(); } -void InstancesHider::show_supports(bool show) { - if (m_show_supports != show) { - m_show_supports = show; - on_update(); - } -} - void InstancesHider::render_cut() const { const SelectionInfo* sel_info = get_pool()->selection_info(); @@ -214,131 +197,39 @@ void InstancesHider::render_cut() const ClippingPlane clp = *get_pool()->object_clipper()->get_clipping_plane(); clp.set_normal(-clp.get_normal()); clipper->set_limiting_plane(clp); - } else + } + else clipper->set_limiting_plane(ClippingPlane::ClipsNothing()); - glsafe(::glPushMatrix()); - if (mv->is_model_part()) - glsafe(::glColor3f(0.8f, 0.3f, 0.0f)); - else { - const std::array& c = color_from_model_volume(*mv); - glsafe(::glColor4f(c[0], c[1], c[2], c[3])); - } glsafe(::glPushAttrib(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST)); - clipper->render_cut(); + clipper->render_cut(mv->is_model_part() ? ColorRGBA(0.8f, 0.3f, 0.0f, 1.0f) : color_from_model_volume(*mv)); glsafe(::glPopAttrib()); - glsafe(::glPopMatrix()); ++clipper_id; } } - -void HollowedMesh::on_update() -{ - const ModelObject* mo = get_pool()->selection_info()->model_object(); - bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; - if (! mo || ! is_sla) - return; - - const GLCanvas3D* canvas = get_pool()->get_canvas(); - const PrintObjects& print_objects = canvas->sla_print()->objects(); - const SLAPrintObject* print_object = m_print_object_idx != -1 - ? print_objects[m_print_object_idx] - : nullptr; - - // Find the respective SLAPrintObject. - if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { - m_print_objects_count = print_objects.size(); - m_print_object_idx = -1; - for (const SLAPrintObject* po : print_objects) { - ++m_print_object_idx; - if (po->model_object()->id() == mo->id()) { - print_object = po; - break; - } - } - } - - // If there is a valid SLAPrintObject, check state of Hollowing step. - if (print_object) { - if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) { - size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; - if (timestamp > m_old_hollowing_timestamp) { - const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice(); - if (! backend_mesh.empty()) { - m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); - Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); - m_hollowed_mesh_transformed->transform(trafo_inv); - m_drainholes = print_object->model_object()->sla_drain_holes; - m_old_hollowing_timestamp = timestamp; - - indexed_triangle_set interior = print_object->hollowed_interior_mesh(); - its_flip_triangles(interior); - m_hollowed_interior_transformed = std::make_unique(std::move(interior)); - m_hollowed_interior_transformed->transform(trafo_inv); - } - else { - m_hollowed_mesh_transformed.reset(nullptr); - } - } - } - else - m_hollowed_mesh_transformed.reset(nullptr); - } -} - - -void HollowedMesh::on_release() -{ - m_hollowed_mesh_transformed.reset(); - m_old_hollowing_timestamp = 0; - m_print_object_idx = -1; -} - - -const TriangleMesh* HollowedMesh::get_hollowed_mesh() const -{ - return m_hollowed_mesh_transformed.get(); -} - -const TriangleMesh* HollowedMesh::get_hollowed_interior() const -{ - return m_hollowed_interior_transformed.get(); -} - - - - void Raycaster::on_update() { wxBusyCursor wait; const ModelObject* mo = get_pool()->selection_info()->model_object(); - if (! mo) + if (mo == nullptr) return; std::vector meshes; const std::vector& mvs = mo->volumes; - if (mvs.size() == 1) { - assert(mvs.front()->is_model_part()); - const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); - if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) - meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); - } - if (meshes.empty()) { - for (const ModelVolume* mv : mvs) { - if (mv->is_model_part()) - meshes.push_back(&mv->mesh()); - } + for (const ModelVolume* mv : mvs) { + if (mv->is_model_part()) + meshes.push_back(&mv->mesh()); } if (meshes != m_old_meshes) { m_raycasters.clear(); for (const TriangleMesh* mesh : meshes) - m_raycasters.emplace_back(new MeshRaycaster(*mesh)); + m_raycasters.emplace_back(new MeshRaycaster(std::make_shared(*mesh))); m_old_meshes = meshes; } } @@ -358,9 +249,6 @@ std::vector Raycaster::raycasters() const } - - - void ObjectClipper::on_update() { const ModelObject* mo = get_pool()->selection_info()->model_object(); @@ -369,24 +257,19 @@ void ObjectClipper::on_update() // which mesh should be cut? std::vector meshes; - bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); - if (has_hollowed) - meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); - - if (meshes.empty()) - for (const ModelVolume* mv : mo->volumes) - meshes.push_back(&mv->mesh()); + std::vector trafos; + for (const ModelVolume* mv : mo->volumes) { + meshes.emplace_back(&mv->mesh()); + trafos.emplace_back(mv->get_transformation()); + } if (meshes != m_old_meshes) { m_clippers.clear(); - for (const TriangleMesh* mesh : meshes) { - m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_mesh(*mesh); + for (size_t i = 0; i < meshes.size(); ++i) { + m_clippers.emplace_back(new MeshClipper, trafos[i]); + m_clippers.back().first->set_mesh(meshes[i]->its); } - m_old_meshes = meshes; - - if (has_hollowed) - m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); + m_old_meshes = std::move(meshes); m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); @@ -403,48 +286,74 @@ void ObjectClipper::on_release() } -void ObjectClipper::render_cut() const +void ObjectClipper::render_cut(const std::vector* ignore_idxs) const { if (m_clp_ratio == 0.) return; const SelectionInfo* sel_info = get_pool()->selection_info(); - const ModelObject* mo = sel_info->model_object(); - Geometry::Transformation inst_trafo; - bool is_assem_cnv = get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView; - inst_trafo = is_assem_cnv ? - mo->instances[sel_info->get_active_instance()]->get_assemble_transformation() : - mo->instances[sel_info->get_active_instance()]->get_transformation(); - auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly(); + const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + + std::vector ignore_idxs_local = ignore_idxs ? *ignore_idxs : std::vector(); - size_t clipper_id = 0; - for (const ModelVolume* mv : mo->volumes) { - Geometry::Transformation vol_trafo = mv->get_transformation(); - Geometry::Transformation trafo = inst_trafo * vol_trafo; - if (is_assem_cnv) { - trafo.set_offset(trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0) + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0)); - } - else { - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - } - auto& clipper = m_clippers[clipper_id]; - clipper->set_plane(*m_clp); - clipper->set_transformation(trafo); - if (is_assem_cnv) - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), std::numeric_limits::max())); - else - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); - glsafe(::glPushMatrix()); - // BBS - glsafe(::glColor3f(0.25f, 0.25f, 0.25f)); - clipper->render_cut(); - glsafe(::glPopMatrix()); - - ++clipper_id; + for (auto& clipper : m_clippers) { + Geometry::Transformation trafo = inst_trafo * clipper.second; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + clipper.first->set_plane(*m_clp); + clipper.first->set_transformation(trafo); + clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + // BBS + clipper.first->render_cut({ 0.25f, 0.25f, 0.25f, 1.0f }, &ignore_idxs_local); + clipper.first->render_contour({ 1.f, 1.f, 1.f, 1.f }, &ignore_idxs_local); + + // Now update the ignore idxs. Find the first element belonging to the next clipper, + // and remove everything before it and decrement everything by current number of contours. + const int num_of_contours = clipper.first->get_number_of_contours(); + ignore_idxs_local.erase(ignore_idxs_local.begin(), std::find_if(ignore_idxs_local.begin(), ignore_idxs_local.end(), [num_of_contours](size_t idx) { return idx >= size_t(num_of_contours); } )); + for (size_t& idx : ignore_idxs_local) + idx -= num_of_contours; } } -void ObjectClipper::set_position(double pos, bool keep_normal) +int ObjectClipper::get_number_of_contours() const +{ + int sum = 0; + for (const auto& [clipper, trafo] : m_clippers) + sum += clipper->get_number_of_contours(); + return sum; +} + +int ObjectClipper::is_projection_inside_cut(const Vec3d& point) const +{ + if (m_clp_ratio == 0.) + return -1; + int idx_offset = 0; + for (const auto& [clipper, trafo] : m_clippers) { + if (int idx = clipper->is_projection_inside_cut(point); idx != -1) + return idx_offset + idx; + idx_offset += clipper->get_number_of_contours(); + } + return -1; +} + +bool ObjectClipper::has_valid_contour() const +{ + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); +} + +std::vector ObjectClipper::point_per_contour() const +{ + std::vector pts; + + for (const auto& clipper : m_clippers) { + const std::vector pts_clipper = clipper.first->point_per_contour(); + pts.insert(pts.end(), pts_clipper.begin(), pts_clipper.end());; + } + return pts; +} + + +void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); int active_inst = get_pool()->selection_info()->get_active_instance(); @@ -479,117 +388,26 @@ void ObjectClipper::set_position(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } -void ObjectClipper::set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos) +void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos) { m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset)); m_clp_ratio = pos; get_pool()->get_canvas()->set_as_dirty(); } -bool ObjectClipper::is_projection_inside_cut(const Vec3d &point) const +const ClippingPlane* ObjectClipper::get_clipping_plane(bool ignore_hide_clipped) const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto &cl) { - return cl->is_projection_inside_cut(point); - }); + static const ClippingPlane no_clip = ClippingPlane::ClipsNothing(); + return (ignore_hide_clipped || m_hide_clipped) ? m_clp.get() : &no_clip; } -bool ObjectClipper::has_valid_contour() const +void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width) { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto &cl) { - return cl->has_valid_contour(); - }); + m_hide_clipped = hide_clipped; + for (auto& clipper : m_clippers) + clipper.first->set_behaviour(fill_cut, contour_width); } -void SupportsClipper::on_update() -{ - const ModelObject* mo = get_pool()->selection_info()->model_object(); - bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; - if (! mo || ! is_sla) - return; - - const GLCanvas3D* canvas = get_pool()->get_canvas(); - const PrintObjects& print_objects = canvas->sla_print()->objects(); - const SLAPrintObject* print_object = m_print_object_idx != -1 - ? print_objects[m_print_object_idx] - : nullptr; - - // Find the respective SLAPrintObject. - if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { - m_print_objects_count = print_objects.size(); - m_print_object_idx = -1; - for (const SLAPrintObject* po : print_objects) { - ++m_print_object_idx; - if (po->model_object()->id() == mo->id()) { - print_object = po; - break; - } - } - } - - if (print_object - && print_object->is_step_done(slaposSupportTree) - && ! print_object->support_mesh().empty()) - { - // If the supports are already calculated, save the timestamp of the respective step - // so we can later tell they were recalculated. - size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - if (! m_clipper || timestamp != m_old_timestamp) { - // The timestamp has changed. - m_clipper.reset(new MeshClipper); - // The mesh should already have the shared vertices calculated. - m_clipper->set_mesh(print_object->support_mesh()); - m_old_timestamp = timestamp; - } - } - else - // The supports are not valid. We better dump the cached data. - m_clipper.reset(); -} - - -void SupportsClipper::on_release() -{ - m_clipper.reset(); - m_old_timestamp = 0; - m_print_object_idx = -1; -} - -void SupportsClipper::render_cut() const -{ - const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); - if (ocl->get_position() == 0. - || ! get_pool()->instances_hider()->are_supports_shown() - || ! m_clipper) - return; - - const SelectionInfo* sel_info = get_pool()->selection_info(); - const ModelObject* mo = sel_info->model_object(); - Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); - //Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation(); - Geometry::Transformation trafo = inst_trafo;// * vol_trafo; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - - // Get transformation of supports - Geometry::Transformation supports_trafo = trafo; - supports_trafo.set_scaling_factor(Vec3d::Ones()); - supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift())); - supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); - // I don't know why, but following seems to be correct. - supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), - 1, - 1.)); - - m_clipper->set_plane(*ocl->get_clipping_plane()); - m_clipper->set_transformation(supports_trafo); - - glsafe(::glPushMatrix()); - glsafe(::glColor3f(1.0f, 0.f, 0.37f)); - m_clipper->render_cut(); - glsafe(::glPopMatrix()); -} - - using namespace AssembleViewDataObjects; AssembleViewDataPool::AssembleViewDataPool(GLCanvas3D* canvas) @@ -696,7 +514,7 @@ void ModelObjectsClipper::on_update() m_clippers.clear(); for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_mesh(*mesh); + m_clippers.back()->set_mesh(mesh->its); } m_old_meshes = meshes; @@ -732,11 +550,8 @@ void ModelObjectsClipper::render_cut() const auto& clipper = m_clippers[clipper_id]; clipper->set_plane(*m_clp); clipper->set_transformation(trafo); - glsafe(::glPushMatrix()); // BBS - glsafe(::glColor3f(0.25f, 0.25f, 0.25f)); - clipper->render_cut(); - glsafe(::glPopMatrix()); + clipper->render_cut({0.25f, 0.25f, 0.25f, 1.0f}); ++clipper_id; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index f4211dfb59..6f2138346e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -1,16 +1,22 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Hejl @hejllukas +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GUI_GLGizmosCommon_hpp_ #define slic3r_GUI_GLGizmosCommon_hpp_ #include #include +#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/MeshUtils.hpp" -#include "libslic3r/SLA/Hollowing.hpp" namespace Slic3r { class ModelObject; - +class ModelInstance; +class SLAPrintObject; +class ModelVolume; namespace GUI { @@ -24,8 +30,12 @@ enum class SLAGizmoEventType : unsigned char { Dragging, Delete, SelectAll, + CtrlDown, + CtrlUp, + ShiftDown, ShiftUp, AltUp, + Escape, ApplyChanges, DiscardChanges, AutomaticGeneration, @@ -66,10 +76,8 @@ enum class CommonGizmosDataID { None = 0, SelectionInfo = 1 << 0, InstancesHider = 1 << 1, - HollowedMesh = 1 << 2, Raycaster = 1 << 3, ObjectClipper = 1 << 4, - SupportsClipper = 1 << 5, }; @@ -79,7 +87,7 @@ enum class CommonGizmosDataID { // by GLGizmoManager, the gizmos keep a pointer to it. class CommonGizmosDataPool { public: - CommonGizmosDataPool(GLCanvas3D* canvas); + explicit CommonGizmosDataPool(GLCanvas3D* canvas); // Update all resources and release what is not used. // Accepts a bitmask of currently required resources. @@ -88,10 +96,10 @@ public: // Getters for the data that need to be accessed from the gizmos directly. CommonGizmosDataObjects::SelectionInfo* selection_info() const; CommonGizmosDataObjects::InstancesHider* instances_hider() const; - CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const; +// CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const; CommonGizmosDataObjects::Raycaster* raycaster() const; CommonGizmosDataObjects::ObjectClipper* object_clipper() const; - CommonGizmosDataObjects::SupportsClipper* supports_clipper() const; + // CommonGizmosDataObjects::SupportsClipper* supports_clipper() const; GLCanvas3D* get_canvas() const { return m_canvas; } @@ -140,7 +148,6 @@ protected: virtual void on_update() = 0; CommonGizmosDataPool* get_pool() const { return m_common; } - private: bool m_is_valid = false; CommonGizmosDataPool* m_common = nullptr; @@ -184,8 +191,6 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - void show_supports(bool show); - bool are_supports_shown() const { return m_show_supports; } void render_cut() const; protected: @@ -193,42 +198,12 @@ protected: void on_release() override; private: - bool m_show_supports = false; std::vector m_old_meshes; std::vector> m_clippers; }; -class HollowedMesh : public CommonGizmosDataBase -{ -public: - explicit HollowedMesh(CommonGizmosDataPool* cgdp) - : CommonGizmosDataBase(cgdp) {} -#ifndef NDEBUG - CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } -#endif // NDEBUG - - const sla::DrainHoles &get_drainholes() const { return m_drainholes; } - - const TriangleMesh* get_hollowed_mesh() const; - const TriangleMesh* get_hollowed_interior() const; - -protected: - void on_update() override; - void on_release() override; - -private: - std::unique_ptr m_hollowed_mesh_transformed; - std::unique_ptr m_hollowed_interior_transformed; - size_t m_old_hollowing_timestamp = 0; - int m_print_object_idx = -1; - int m_print_objects_count = 0; - sla::DrainHoles m_drainholes; -}; - - - class Raycaster : public CommonGizmosDataBase { public: @@ -260,57 +235,31 @@ public: #ifndef NDEBUG CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - - void set_position(double pos, bool keep_normal); double get_position() const { return m_clp_ratio; } - ClippingPlane* get_clipping_plane() const { return m_clp.get(); } - void render_cut() const; + const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const; + void render_cut(const std::vector* ignore_idxs = nullptr) const; + void set_position_by_ratio(double pos, bool keep_normal); + void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); + void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); + + int get_number_of_contours() const; + std::vector point_per_contour() const; - void set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos); - - bool is_projection_inside_cut(const Vec3d &point_in) const; + int is_projection_inside_cut(const Vec3d& point_in) const; bool has_valid_contour() const; + protected: void on_update() override; void on_release() override; private: std::vector m_old_meshes; - std::vector> m_clippers; + std::vector, Geometry::Transformation>> m_clippers; std::unique_ptr m_clp; double m_clp_ratio = 0.; double m_active_inst_bb_radius = 0.; -}; - - - -class SupportsClipper : public CommonGizmosDataBase -{ -public: - explicit SupportsClipper(CommonGizmosDataPool* cgdp) - : CommonGizmosDataBase(cgdp) {} -#ifndef NDEBUG - CommonGizmosDataID get_dependencies() const override { - return CommonGizmosDataID( - int(CommonGizmosDataID::SelectionInfo) - | int(CommonGizmosDataID::ObjectClipper) - ); - } -#endif // NDEBUG - - void render_cut() const; - - -protected: - void on_update() override; - void on_release() override; - -private: - size_t m_old_timestamp = 0; - int m_print_object_idx = -1; - int m_print_objects_count = 0; - std::unique_ptr m_clipper; + bool m_hide_clipped = true; }; } // namespace CommonGizmosDataObjects diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 87c9a00c9e..3398362855 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1,3 +1,8 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas +///|/ Copyright (c) 2019 John Drake @foxox +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "GLGizmosManager.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" @@ -13,15 +18,15 @@ #include "slic3r/GUI/Gizmos/GLGizmoScale.hpp" #include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" -#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" +//#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" -// BBS -#include "slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp" -#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp" -#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" +//#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp" +//#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" #include "slic3r/GUI/Gizmos/GLGizmoText.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp" @@ -45,6 +50,7 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) , m_enabled(false) , m_icons_texture_dirty(true) , m_current(Undefined) + , m_hover(Undefined) , m_tooltip("") , m_serializing(false) //BBS: GUI refactor: add object manipulation in gizmo @@ -55,6 +61,7 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) std::vector GLGizmosManager::get_selectable_idxs() const { std::vector out; + out.reserve(m_gizmos.size()); if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { for (size_t i = 0; i < m_gizmos.size(); ++i) if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation) @@ -69,23 +76,23 @@ std::vector GLGizmosManager::get_selectable_idxs() const } //BBS: GUI refactor: GLToolbar&&Gizmo adjust -//when judge the mouse position, {0, 0} is at the left-up corner, and no need to multiply camera_zoom -size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const +GLGizmosManager::EType GLGizmosManager::get_gizmo_from_mouse(const Vec2d &mouse_pos) const { if (! m_enabled) return Undefined; - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_scaled_total_height(); + float cnv_h = (float) m_parent.get_canvas_size().get_height(); + float height = get_scaled_total_height(); float icons_size = m_layout.scaled_icons_size(); - float border = m_layout.scaled_border(); + float border = m_layout.scaled_border(); //BBS: GUI refactor: GLToolbar&&Gizmo adjust float cnv_w = (float)m_parent.get_canvas_size().get_width(); float width = get_scaled_total_width(); -#if BBS_TOOLBAR_ON_TOP //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();; - float top_x = std::max(m_parent.get_main_toolbar_width() + border, 0.5f * (cnv_w - width + m_parent.get_main_toolbar_width() + m_parent.get_collapse_toolbar_width() - m_parent.get_assemble_view_toolbar_width()) + border); + const float separator_width = m_parent.get_separator_toolbar_width(); + float top_x = std::max(0.0f, 0.5f * (cnv_w - (width + separator_width + m_parent.get_main_toolbar_width() - m_parent.get_collapse_toolbar_width() + m_parent.get_assemble_view_toolbar_width()))); + top_x += m_parent.get_main_toolbar_width() + separator_width / 2 + border; if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) top_x = 0.5f * cnv_w + 0.5f * (m_parent.get_assembly_paint_toolbar_width()); float top_y = 0; @@ -102,31 +109,9 @@ size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const if ((float)mouse_pos(0) <= top_x + from_left * stride_x + icons_size) { std::vector selectable = get_selectable_idxs(); if (from_left < selectable.size()) - return selectable[from_left]; + return static_cast(selectable[from_left]); } } -#else - //float top_y = 0.5f * (cnv_h - height) + border; - float top_x = cnv_w - width; - float top_y = 0.5f * (cnv_h - height + m_parent.get_main_toolbar_height() - m_parent.get_assemble_view_toolbar_width()) + border; - float stride_y = m_layout.scaled_stride_y(); - - // is mouse horizontally in the area? - //if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) { - if (((top_x + border) <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= (top_x + border + icons_size))) { - // which icon is it on? - size_t from_top = (size_t)((float)mouse_pos(1) - top_y) / stride_y; - if (from_top < 0) - return Undefined; - // is it really on the icon or already past the border? - if ((float)mouse_pos(1) <= top_y + from_top * stride_y + icons_size) { - std::vector selectable = get_selectable_idxs(); - if (from_top < selectable.size()) - return selectable[from_top]; - } - } -#endif - return Undefined; } @@ -174,6 +159,9 @@ void GLGizmosManager::switch_gizmos_icon_filename() case(EType::MeshBoolean): gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); break; + case(EType::Measure): + gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg"); + break; } } @@ -204,12 +192,13 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, m_is_dark ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg", EType::Rotate, &m_object_manipulation)); m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten)); - m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut)); + m_gizmos.emplace_back(new GLGizmoCut3D(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut)); m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify)); //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++)); @@ -289,20 +278,13 @@ float GLGizmosManager::get_layout_scale() return m_layout.scale; } -bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_texture) +bool GLGizmosManager::init_arrow(const std::string& filename) { - if (m_arrow_texture.texture.get_id() != 0) + if (m_arrow_texture.get_id() != 0) return true; - std::string path = resources_dir() + "/images/"; - bool res = false; - - if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); - if (res) - m_arrow_texture.metadata = arrow_texture; - - return res; + const std::string path = resources_dir() + "/images/"; + return (!filename.empty()) ? m_arrow_texture.load_from_svg_file(path + filename, false, false, false, 1000) : false; } void GLGizmosManager::set_overlay_icon_size(float size) @@ -328,9 +310,9 @@ void GLGizmosManager::refresh_on_off_state() if (m_serializing || m_current == Undefined || m_gizmos.empty()) return; - if (m_current != Undefined - && ! m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined)) - update_data(); + // FS: Why update data after Undefined gizmo activation? + if (!m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined)) + update_data(); } void GLGizmosManager::reset_all_states() @@ -344,9 +326,13 @@ void GLGizmosManager::reset_all_states() bool GLGizmosManager::open_gizmo(EType type) { - int idx = int(type); - if (m_gizmos[idx]->is_activable() - && activate_gizmo(m_current == idx ? Undefined : (EType)idx)) { + int idx = static_cast(type); + + // re-open same type cause closing + if (m_current == type) type = Undefined; + + if (m_gizmos[idx]->is_activable() && activate_gizmo(type)) { + // remove update data into gizmo itself update_data(); #ifdef __WXOSX__ m_parent.post_event(SimpleEvent(wxEVT_PAINT)); @@ -377,27 +363,6 @@ void GLGizmosManager::set_hover_id(int id) m_gizmos[m_current]->set_hover_id(id); } -void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) -{ - if (!m_enabled || type == Undefined || m_gizmos.empty()) - return; - - if (enable) - m_gizmos[type]->enable_grabber(id); - else - m_gizmos[type]->disable_grabber(id); -} - -void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) -{ - if (!m_enabled) - return; - - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos)); -} - void GLGizmosManager::update_assemble_view_data() { if (m_assemble_view_data) { @@ -410,70 +375,12 @@ void GLGizmosManager::update_assemble_view_data() void GLGizmosManager::update_data() { - if (!m_enabled) - return; - - const Selection& selection = m_parent.get_selection(); - - bool is_wipe_tower = selection.is_wipe_tower(); - enable_grabber(Move, 2, !is_wipe_tower); - enable_grabber(Rotate, 0, !is_wipe_tower); - enable_grabber(Rotate, 1, !is_wipe_tower); - - // BBS: when select multiple objects, uniform scale can be deselected, display the 0-5 grabbers - //bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); - //for (unsigned int i = 0; i < 6; ++i) - //{ - // enable_grabber(Scale, i, enable_scale_xyz); - //} - - if (m_common_gizmos_data) { + if (!m_enabled) return; + if (m_common_gizmos_data) m_common_gizmos_data->update(get_current() - ? get_current()->get_requirements() - : CommonGizmosDataID(0)); - } - - if (selection.is_single_full_instance()) - { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - set_scale(volume->get_instance_scaling_factor()); - set_rotation(Vec3d::Zero()); - // BBS - finish_cut_rotation(); - ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; - set_flattening_data(model_object); - set_sla_support_data(model_object); - set_painter_gizmo_data(); - } - else if (selection.is_single_volume() || selection.is_single_modifier()) - { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - set_scale(volume->get_volume_scaling_factor()); - set_rotation(Vec3d::Zero()); - // BBS - finish_cut_rotation(); - set_flattening_data(nullptr); - set_sla_support_data(nullptr); - set_painter_gizmo_data(); - } - else if (is_wipe_tower) - { - DynamicPrintConfig& proj_cfg = wxGetApp().preset_bundle->project_config; - set_scale(Vec3d::Ones()); - set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(proj_cfg.option("wipe_tower_rotation_angle"))->value)); - set_flattening_data(nullptr); - set_sla_support_data(nullptr); - set_painter_gizmo_data(); - } - else - { - set_scale(Vec3d::Ones()); - set_rotation(Vec3d::Zero()); - set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); - set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); - set_painter_gizmo_data(); - } + ? get_current()->get_requirements() + : CommonGizmosDataID(0)); + if (m_current != Undefined) m_gizmos[m_current]->data_changed(m_serializing); //BBS: GUI refactor: add object manipulation in gizmo m_object_manipulation.update_ui_from_settings(); @@ -517,124 +424,17 @@ bool GLGizmosManager::is_dragging() const return m_gizmos[m_current]->is_dragging(); } -void GLGizmosManager::start_dragging() -{ - if (! m_enabled || m_current == Undefined) - return; - m_gizmos[m_current]->start_dragging(); -} - -void GLGizmosManager::stop_dragging() -{ - if (! m_enabled || m_current == Undefined) - return; - - m_gizmos[m_current]->stop_dragging(); -} - -Vec3d GLGizmosManager::get_displacement() const -{ - if (!m_enabled) - return Vec3d::Zero(); - - return dynamic_cast(m_gizmos[Move].get())->get_displacement(); -} - -Vec3d GLGizmosManager::get_scale() const -{ - if (!m_enabled) - return Vec3d::Ones(); - - return dynamic_cast(m_gizmos[Scale].get())->get_scale(); -} - -void GLGizmosManager::set_scale(const Vec3d& scale) -{ - if (!m_enabled || m_gizmos.empty()) - return; - - dynamic_cast(m_gizmos[Scale].get())->set_scale(scale); -} - -Vec3d GLGizmosManager::get_scale_offset() const -{ - if (!m_enabled || m_gizmos.empty()) - return Vec3d::Zero(); - - return dynamic_cast(m_gizmos[Scale].get())->get_offset(); -} - -Vec3d GLGizmosManager::get_rotation() const -{ - if (!m_enabled || m_gizmos.empty()) - return Vec3d::Zero(); - - return dynamic_cast(m_gizmos[Rotate].get())->get_rotation(); -} - -void GLGizmosManager::set_rotation(const Vec3d& rotation) -{ - if (!m_enabled || m_gizmos.empty()) - return; - dynamic_cast(m_gizmos[Rotate].get())->set_rotation(rotation); -} - -// BBS -void GLGizmosManager::finish_cut_rotation() -{ - dynamic_cast(m_gizmos[Cut].get())->finish_rotation(); -} - -Vec3d GLGizmosManager::get_flattening_normal() const -{ - if (!m_enabled || m_gizmos.empty()) - return Vec3d::Zero(); - - return dynamic_cast(m_gizmos[Flatten].get())->get_flattening_normal(); -} - -void GLGizmosManager::set_flattening_data(const ModelObject* model_object) -{ - if (!m_enabled || m_gizmos.empty()) - return; - - dynamic_cast(m_gizmos[Flatten].get())->set_flattening_data(model_object); -} - -void GLGizmosManager::set_sla_support_data(ModelObject* model_object) -{ - if (! m_enabled - || m_gizmos.empty() - || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) - return; - - auto* gizmo_hollow = dynamic_cast(m_gizmos[Hollow].get()); - auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); - gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection()); - gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); -} - -void GLGizmosManager::set_painter_gizmo_data() -{ - if (!m_enabled || m_gizmos.empty()) - return; - - dynamic_cast(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection()); - dynamic_cast(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection()); - dynamic_cast(m_gizmos[MmuSegmentation].get())->set_painter_gizmo_data(m_parent.get_selection()); -} - // Returns true if the gizmo used the event to do something, false otherwise. bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (!m_enabled || m_gizmos.empty()) return false; - if (m_current == SlaSupports) + /*if (m_current == SlaSupports) return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Hollow) return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == FdmSupports) + else*/ if (m_current == FdmSupports) return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Seam) return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); @@ -642,8 +442,10 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Text) return dynamic_cast(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Measure) + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) - return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MeshBoolean) return dynamic_cast(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else @@ -676,8 +478,9 @@ ClippingPlane GLGizmosManager::get_assemble_view_clipping_plane() const bool GLGizmosManager::wants_reslice_supports_on_undo() const { - return (m_current == SlaSupports - && dynamic_cast(m_gizmos.at(SlaSupports).get())->has_backend_supports()); + return false; + // return (m_current == SlaSupports + // && dynamic_cast(m_gizmos.at(SlaSupports).get())->has_backend_supports()); } void GLGizmosManager::on_change_color_mode(bool is_dark) { @@ -692,7 +495,7 @@ void GLGizmosManager::render_current_gizmo() const m_gizmos[m_current]->render(); } -void GLGizmosManager::render_painter_gizmo() const +void GLGizmosManager::render_painter_gizmo() { // This function shall only be called when current gizmo is // derived from GLGizmoPainterBase. @@ -711,15 +514,6 @@ void GLGizmosManager::render_painter_assemble_view() const m_assemble_view_data->model_objects_clipper()->render_cut(); } -void GLGizmosManager::render_current_gizmo_for_picking_pass() const -{ - if (! m_enabled || m_current == Undefined) - - return; - - m_gizmos[m_current]->render_for_picking(); -} - void GLGizmosManager::render_overlay() { if (!m_enabled) @@ -740,11 +534,11 @@ std::string GLGizmosManager::get_tooltip() const return (curr != nullptr) ? curr->get_tooltip() : ""; } -bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) +bool GLGizmosManager::on_mouse_wheel(const wxMouseEvent &evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { + if (/*m_current == SlaSupports || m_current == Hollow ||*/ m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown() // BBS @@ -760,253 +554,113 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) return processed; } -bool GLGizmosManager::on_mouse(wxMouseEvent& evt) -{ - // used to set a right up event as processed when needed - static bool pending_right_up = false; - - Point pos(evt.GetX(), evt.GetY()); - Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); - - Selection& selection = m_parent.get_selection(); - int selected_object_idx = selection.get_object_idx(); - bool processed = false; - - // when control is down we allow scene pan and rotation even when clicking over some object - bool control_down = evt.CmdDown(); - - // mouse anywhere - if (evt.Moving()) { - m_tooltip = update_hover_state(mouse_pos); - if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text) - // BBS - gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); - } else if (evt.LeftUp()) { - if (m_mouse_capture.left) { - processed = true; - m_mouse_capture.left = false; +bool GLGizmosManager::gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event) { + assert(m_enabled); + // keep information about events to process + struct MouseCapture + { + bool left = false; + bool middle = false; + bool right = false; + bool exist_tooltip = false; + MouseCapture() = default; + bool any() const { return left || middle || right; } + void reset() { + left = false; + middle = false; + right = false; } - else if (is_dragging()) { - switch (m_current) { - case Move: { m_parent.do_move(("Tool-Move")); break; } - case Scale: { m_parent.do_scale(("Tool-Scale")); break; } - case Rotate: { m_parent.do_rotate(("Tool-Rotate")); break; } - default: break; - } + }; + static MouseCapture mc; - stop_dragging(); - update_data(); + // wxCoord == int --> wx/types.h + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - // Let the plater know that the dragging finished, so a delayed refresh - // of the scene with the background processing data should be performed. - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // updates camera target constraints - m_parent.refresh_camera_scene_box(); + EType gizmo = get_gizmo_from_mouse(mouse_pos); + bool selected_gizmo = gizmo != Undefined; - processed = true; - } -// else -// return false; - } - else if (evt.MiddleUp()) { - if (m_mouse_capture.middle) { - processed = true; - m_mouse_capture.middle = false; - } - else + // fast reaction on move mouse + if (mouse_event.Moving()) { + assert(!mc.any()); + if (selected_gizmo) { + mc.exist_tooltip = true; + update_hover_state(gizmo); + // at this moment is enebled to process mouse move under gizmo + // tools bar e.g. Do not interupt dragging. return false; + } else if (mc.exist_tooltip) { + // first move out of gizmo tool bar - unselect tooltip + mc.exist_tooltip = false; + update_hover_state(Undefined); + return false; + } + return false; } - else if (evt.RightUp()) { - if (pending_right_up) { - pending_right_up = false; + + if (selected_gizmo) { + // mouse is above toolbar + if (mouse_event.LeftDown() || mouse_event.LeftDClick()) { + mc.left = true; + open_gizmo(gizmo); return true; } - if (m_mouse_capture.right) { - processed = true; - m_mouse_capture.right = false; + else if (mouse_event.RightDown()) { + mc.right = true; + return true; } -// else -// return false; - } - else if (evt.Dragging() && !is_dragging()) { - if (m_mouse_capture.any()) - // if the button down was done on this toolbar, prevent from dragging into the scene - processed = true; -// else -// return false; - } - else if (evt.Dragging() && is_dragging()) { - if (!m_parent.get_wxglcanvas()->HasCapture()) - m_parent.get_wxglcanvas()->CaptureMouse(); - - m_parent.set_mouse_as_dragging(); - update(m_parent.mouse_ray(pos), pos); - - switch (m_current) - { - case Move: - { - // Apply new temporary offset - selection.translate(get_displacement()); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - break; - } - case Scale: - { - // Apply new temporary scale factors - TransformationType transformation_type(TransformationType::Local_Absolute_Joint); - //if (evt.AltDown()) - // transformation_type.set_independent(); - selection.scale(get_scale(), transformation_type); - if (control_down && m_gizmos[m_current].get()->get_hover_id() < 6) - selection.translate(get_scale_offset(), true); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - break; - } - case Rotate: - { - // Apply new temporary rotations - TransformationType transformation_type(TransformationType::World_Relative_Joint); - if (evt.AltDown()) - transformation_type.set_independent(); - selection.rotate(get_rotation(), transformation_type); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - break; - } - default: - break; - } - - m_parent.set_as_dirty(); - processed = true; - } - - if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) { - // mouse is outside the toolbar - m_tooltip.clear(); - - if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || - m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean) - && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) - // the gizmo got the event and took some action, there is no need to do anything more - processed = true; - else if (!selection.is_empty() && grabber_contains_mouse()) { - update_data(); - selection.start_dragging(); - start_dragging(); - - // Let the plater know that the dragging started - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); - - if (m_current == Flatten) { - // Rotate the object so the normal points downward: - m_parent.do_flatten(get_flattening_normal(), L("Tool-Lay on Face")); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - } - - m_parent.set_as_dirty(); - processed = true; - } - } - else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow) - && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object - pending_right_up = true; - // event was taken care of by the SlaSupports gizmo - processed = true; - } - else if (evt.RightDown() && !control_down && selected_object_idx != -1 - && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) - && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // event was taken care of by the FdmSupports / Seam / MMUPainting gizmo - processed = true; - } - else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)) - // don't allow dragging objects with the Sla gizmo on - processed = true; - else if (evt.Dragging() && !control_down - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) - && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { - // the gizmo got the event and took some action, no need to do anything more here - m_parent.set_as_dirty(); - processed = true; - } - else if (evt.Dragging() && control_down && (evt.LeftIsDown() || evt.RightIsDown())) { - // CTRL has been pressed while already dragging -> stop current action - if (evt.LeftIsDown()) - gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); - else if (evt.RightIsDown()) - gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); - } - else if (evt.LeftUp() - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) - && !m_parent.is_mouse_dragging() - && gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down)) { - // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither - // object moving or selecting is suppressed in that case - processed = true; - } - else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) { - // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active - selection.stop_dragging(); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - processed = true; - } - else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) - && !m_parent.is_mouse_dragging() - && gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down)) { - processed = true; - } - else if (evt.LeftUp()) { - selection.stop_dragging(); - // BBS - //wxGetApp().obj_manipul()->set_dirty(); - } - } - else { - // mouse inside toolbar - if (evt.LeftDown() || evt.LeftDClick()) { - m_mouse_capture.left = true; - m_mouse_capture.parent = &m_parent; - processed = true; - if (!selection.is_empty()) { - update_on_off_state(mouse_pos); - update_data(); - m_parent.set_as_dirty(); - try { - if ((int)m_hover >= 0 && (int)m_hover < m_gizmos.size()) { - std::string name = get_name_from_gizmo_etype(m_hover); - int count = m_gizmos[m_hover]->get_count(); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) { - agent->track_update_property(name, std::to_string(count)); - } - } - } - catch (...) {} - } - } - else if (evt.MiddleDown()) { - m_mouse_capture.middle = true; - m_mouse_capture.parent = &m_parent; - } - else if (evt.RightDown()) { - m_mouse_capture.right = true; - m_mouse_capture.parent = &m_parent; + else if (mouse_event.MiddleDown()) { + mc.middle = true; return true; } } - return processed; + if (mc.any()) { + // Check if exist release of event started above toolbar? + if (mouse_event.Dragging()) { + if (!selected_gizmo && mc.exist_tooltip) { + // dragging out of gizmo let tooltip disapear + mc.exist_tooltip = false; + update_hover_state(Undefined); + } + // draging start on toolbar so no propagation into scene + return true; + } + else if (mc.left && mouse_event.LeftUp()) { + mc.left = false; + return true; + } + else if (mc.right && mouse_event.RightUp()) { + mc.right = false; + return true; + } + else if (mc.middle && mouse_event.MiddleUp()) { + mc.middle = false; + return true; + } + + // event out of window is not porocessed + // left down on gizmo -> keep down -> move out of window -> release left + if (mouse_event.Leaving()) mc.reset(); + } + return false; +} + +bool GLGizmosManager::on_mouse(const wxMouseEvent &mouse_event) +{ + if (!m_enabled) return false; + + // tool bar wants to use event? + if (gizmos_toolbar_on_mouse(mouse_event)) return true; + + // current gizmo wants to use event? + if (m_current != Undefined && + // check if gizmo override method could be slower than simple call virtual function + // &m_gizmos[m_current]->on_mouse != &GLGizmoBase::on_mouse && + m_gizmos[m_current]->on_mouse(mouse_event)) + return true; + + return false; } bool GLGizmosManager::on_char(wxKeyEvent& evt) @@ -1017,8 +671,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) bool processed = false; - if ((evt.GetModifiers() & ctrlMask) != 0) - { + if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { #ifdef __APPLE__ @@ -1036,23 +689,31 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } } - else if (!evt.HasModifiers()) - { + else if (!evt.HasModifiers()) { switch (keyCode) { // key ESC case WXK_ESCAPE: { - if (m_current != Undefined) - { - if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) + if (m_current != Undefined) { + if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) { + // do nothing + } else + //if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) reset_all_states(); processed = true; } break; } - //skip some keys when gizmo + case WXK_BACK: + case WXK_DELETE: + { + if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete)) + processed = true; + + break; + } case 'A': case 'a': { @@ -1130,8 +791,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } - if (!processed && !evt.HasModifiers()) - { + if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) processed = true; } @@ -1147,16 +807,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; - // todo: zhimin Each gizmo should handle key event in it own on_key() function - if (m_current == Cut) { - if (GLGizmoAdvancedCut *gizmo_cut = dynamic_cast(get_current())) { - return gizmo_cut->on_key(evt); - } - } - - if (evt.GetEventType() == wxEVT_KEY_UP) - { - if (m_current == SlaSupports || m_current == Hollow) + if (evt.GetEventType() == wxEVT_KEY_UP) { + /*if (m_current == SlaSupports || m_current == Hollow) { bool is_editing = true; bool is_rectangle_dragging = false; @@ -1189,6 +841,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) // capture number key processed = true; } + }*/ + if (m_current == Measure) { + if (keyCode == WXK_CONTROL) + gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); + else if (keyCode == WXK_SHIFT) + gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); } // if (processed) @@ -1196,19 +854,16 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { - if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) + /*if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && dynamic_cast(get_current())->is_in_editing_mode()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } - else if (m_current == Cut) - { - // BBS -#if 0 + else*/ if (m_current == Cut) { auto do_move = [this, &processed](double delta_z) { - GLGizmoAdvancedCut* cut = dynamic_cast(get_current()); - cut->set_cut_z(delta_z + cut->get_cut_z()); + GLGizmoCut3D* cut = dynamic_cast(get_current()); + cut->shift_cut(delta_z); processed = true; }; @@ -1216,10 +871,13 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) { case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; } case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } + case WXK_SHIFT : case WXK_ALT: { + processed = get_current()->is_in_editing_mode(); + } default: { break; } } -#endif - } else if (m_current == Simplify && keyCode == WXK_ESCAPE) { + } + else if (m_current == Simplify && keyCode == WXK_ESCAPE) { GLGizmoSimplify *simplify = dynamic_cast(get_current()); if (simplify != nullptr) processed = simplify->on_esc_key_down(); @@ -1260,6 +918,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) wxGetApp().imgui()->set_requires_extra_frame(); } } + else if (m_current == Measure) { + if (keyCode == WXK_CONTROL) + gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); + else if (keyCode == WXK_SHIFT) + gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); + } } if (processed) @@ -1272,47 +936,43 @@ void GLGizmosManager::update_after_undo_redo(const UndoRedo::Snapshot& snapshot) { update_data(); m_serializing = false; - if (m_current == SlaSupports - && snapshot.snapshot_data.flags & UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS) - dynamic_cast(m_gizmos[SlaSupports].get())->reslice_SLA_supports(true); + // if (m_current == SlaSupports + // && snapshot.snapshot_data.flags & UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS) + // dynamic_cast(m_gizmos[SlaSupports].get())->reslice_SLA_supports(true); } -void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border) const +void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border_w, float border_h) const { - unsigned int tex_id = m_background_texture.texture.get_id(); - float tex_width = (float)m_background_texture.texture.get_width(); - float tex_height = (float)m_background_texture.texture.get_height(); - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) - { + const unsigned int tex_id = m_background_texture.texture.get_id(); + const float tex_width = float(m_background_texture.texture.get_width()); + const float tex_height = float(m_background_texture.texture.get_height()); + if (tex_id != 0 && tex_width > 0 && tex_height > 0) { //BBS: GUI refactor: remove the corners of gizmo - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + const float inv_tex_width = 1.0f / tex_width; + const float inv_tex_height = 1.0f / tex_height; - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; + const float internal_left_uv = float(m_background_texture.metadata.left) * inv_tex_width; + const float internal_right_uv = 1.0f - float(m_background_texture.metadata.right) * inv_tex_width; + const float internal_top_uv = 1.0f - float(m_background_texture.metadata.top) * inv_tex_height; + const float internal_bottom_uv = float(m_background_texture.metadata.bottom) * inv_tex_height; GLTexture::render_sub_texture(tex_id, left, right, bottom, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); /* - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left = left + border; - float internal_right = right - border; - float internal_top = top - border; - float internal_bottom = bottom + border; + const float internal_left = left + border_w; + const float internal_right = right - border_w; + const float internal_top = top - border_h; + const float internal_bottom = bottom + border_h; // float left_uv = 0.0f; - float right_uv = 1.0f; - float top_uv = 1.0f; - float bottom_uv = 0.0f; + const float right_uv = 1.0f; + const float top_uv = 1.0f; + const float bottom_uv = 0.0f; - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; + const float internal_left_uv = float(m_background_texture.metadata.left) * inv_tex_width; + const float internal_right_uv = 1.0f - float(m_background_texture.metadata.right) * inv_tex_width; + const float internal_top_uv = 1.0f - float(m_background_texture.metadata.top) * inv_tex_height; + const float internal_bottom_uv = float(m_background_texture.metadata.bottom) * inv_tex_height; // top-left corner GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); @@ -1346,7 +1006,7 @@ void GLGizmosManager::render_background(float left, float top, float right, floa void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_type) const { - std::vector selectable_idxs = get_selectable_idxs(); + const std::vector selectable_idxs = get_selectable_idxs(); if (selectable_idxs.empty()) return; float cnv_w = (float)m_parent.get_canvas_size().get_width(); @@ -1365,18 +1025,18 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t if (idx == highlighted_type) { int tex_width = m_icons_texture.get_width(); int tex_height = m_icons_texture.get_height(); - unsigned int tex_id = m_arrow_texture.texture.get_id(); + unsigned int tex_id = m_arrow_texture.get_id(); float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height; + const float left_uv = 0.0f; + const float right_uv = 1.0f; + const float top_uv = 1.0f; + const float bottom_uv = 0.0f; - float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); + float arrow_sides_ratio = (float)m_arrow_texture.get_height() / (float)m_arrow_texture.get_width(); - GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); + GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { left_uv, bottom_uv }, { left_uv, top_uv }, { right_uv, top_uv }, { right_uv, bottom_uv } }); break; } zoomed_top_y -= zoomed_stride_y; @@ -1387,119 +1047,98 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t //when rendering, {0, 0} is at the center, {-0.5, 0.5} at the left-top void GLGizmosManager::do_render_overlay() const { - std::vector selectable_idxs = get_selectable_idxs(); + const std::vector selectable_idxs = get_selectable_idxs(); if (selectable_idxs.empty()) return; - float cnv_w = (float)m_parent.get_canvas_size().get_width(); - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); + const Size cnv_size = m_parent.get_canvas_size(); + const float cnv_w = (float)cnv_size.get_width(); + const float cnv_h = (float)cnv_size.get_height(); - float height = get_scaled_total_height(); - float width = get_scaled_total_width(); - float zoomed_border = m_layout.scaled_border() * inv_zoom; + if (cnv_w == 0 || cnv_h == 0) + return; - float zoomed_top_x; + const float inv_cnv_w = 1.0f / cnv_w; + const float inv_cnv_h = 1.0f / cnv_h; + + const float height = 2.0f * get_scaled_total_height() * inv_cnv_h; + const float width = 2.0f * get_scaled_total_width() * inv_cnv_w; + const float border_h = 2.0f * m_layout.scaled_border() * inv_cnv_h; + const float border_w = 2.0f * m_layout.scaled_border() * inv_cnv_w; + + float top_x; if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { - zoomed_top_x = 0.5f * m_parent.get_assembly_paint_toolbar_width() * inv_zoom; + top_x = m_parent.get_assembly_paint_toolbar_width() * inv_cnv_w; } else { //BBS: GUI refactor: GLToolbar&&Gizmo adjust -#if BBS_TOOLBAR_ON_TOP float main_toolbar_width = (float)m_parent.get_main_toolbar_width(); float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width(); float collapse_width = (float)m_parent.get_collapse_toolbar_width(); + float separator_width = (float)m_parent.get_separator_toolbar_width(); //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); //float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom; - float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width - collapse_width)) * inv_zoom; + float main_toolbar_left = std::max(-0.5f * cnv_w, -0.5f * (main_toolbar_width + get_scaled_total_width() + assemble_view_width + separator_width - collapse_width)); //float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom; - zoomed_top_x = main_toolbar_left + (main_toolbar_width)*inv_zoom; + top_x = main_toolbar_left + main_toolbar_width + separator_width / 2; + top_x = top_x * inv_cnv_w * 2; } - float zoomed_top_y = 0.5f * cnv_h * inv_zoom; -#else - //float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom; - //float zoomed_top_y = (0.5f * height) * inv_zoom; - float zoomed_top_x = (0.5f * cnv_w - width) * inv_zoom; - float main_toolbar_height = (float)m_parent.get_main_toolbar_height(); - float assemble_view_height = (float)m_parent.get_assemble_view_toolbar_height(); - //float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float zoomed_top_y = 0.5f * (height + assemble_view_height - main_toolbar_height) * inv_zoom; -#endif - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": zoomed_top_y %1%, space_height %2%, main_toolbar_height %3% zoomed_top_x %4%") % zoomed_top_y % space_height % main_toolbar_height % zoomed_top_x; + float top_y = 1.0f; - float zoomed_left = zoomed_top_x; - float zoomed_top = zoomed_top_y; - float zoomed_right = zoomed_left + width * inv_zoom; - float zoomed_bottom = zoomed_top - height * inv_zoom; + render_background(top_x, top_y, top_x + width, top_y - height, border_w, border_h); - render_background(zoomed_left, zoomed_top, zoomed_right, zoomed_bottom, zoomed_border); + top_x += border_w; + top_y -= border_h; - zoomed_top_x += zoomed_border; - zoomed_top_y -= zoomed_border; - - float icons_size = m_layout.scaled_icons_size(); - float zoomed_icons_size = icons_size * inv_zoom; - float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom; + const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w; + const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h; //BBS: GUI refactor: GLToolbar&&Gizmo adjust - float zoomed_stride_x = m_layout.scaled_stride_x() * inv_zoom; + const float stride_x = 2.0f * m_layout.scaled_stride_x() * inv_cnv_w; - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); + const unsigned int icons_texture_id = m_icons_texture.get_id(); + const int tex_width = m_icons_texture.get_width(); + const int tex_height = m_icons_texture.get_height(); - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)) + if (icons_texture_id == 0 || tex_width <= 1 || tex_height <= 1) return; - float du = (float)(tex_width - 1) / (6.0f * (float)tex_width); // 6 is the number of possible states if the icons - float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height); + const float du = (float)(tex_width - 1) / (6.0f * (float)tex_width); // 6 is the number of possible states if the icons + const float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height); // tiles in the texture are spaced by 1 pixel - float u_offset = 1.0f / (float)tex_width; - float v_offset = 1.0f / (float)tex_height; + const float u_offset = 1.0f / (float)tex_width; + const float v_offset = 1.0f / (float)tex_height; bool is_render_current = false; - for (size_t idx : selectable_idxs) - { + for (size_t idx : selectable_idxs) { GLGizmoBase* gizmo = m_gizmos[idx].get(); - unsigned int sprite_id = gizmo->get_sprite_id(); + const unsigned int sprite_id = gizmo->get_sprite_id(); // higlighted state needs to be decided first so its highlighting in every other state - int icon_idx = (m_highlight.first == idx ? (m_highlight.second ? 4 : 5) : (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3))); + const int icon_idx = (m_highlight.first == idx ? (m_highlight.second ? 4 : 5) : (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3))); - float v_top = v_offset + sprite_id * dv; - float u_left = u_offset + icon_idx * du; - float v_bottom = v_top + dv - v_offset; - float u_right = u_left + du - u_offset; - - GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); + const float u_left = u_offset + icon_idx * du; + const float u_right = u_left + du - u_offset; + const float v_top = v_offset + sprite_id * dv; + const float v_bottom = v_top + dv - v_offset; + GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + icons_size_x, top_y - icons_size_y, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (idx == m_current) { //BBS: GUI refactor: GLToolbar&&Gizmo adjust //render_input_window uses a different coordination(imgui) //1. no need to scale by camera zoom, set {0,0} at left-up corner for imgui -#if BBS_TOOLBAR_ON_TOP //gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); - gizmo->render_input_window(0.5 * cnv_w + zoomed_top_x * zoom, height, cnv_h); + gizmo->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h); is_render_current = true; -#else - float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); - //gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); - gizmo->render_input_window(cnv_w - width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); -#endif } -#if BBS_TOOLBAR_ON_TOP - zoomed_top_x += zoomed_stride_x; -#else - zoomed_top_y -= zoomed_stride_y; -#endif + top_x += stride_x; } // BBS simplify gizmo is not a selected gizmo and need to render input window if (!is_render_current && m_current != Undefined) { - m_gizmos[m_current]->render_input_window(0.5 * cnv_w + zoomed_top_x * zoom, height, cnv_h); + m_gizmos[m_current]->render_input_window(0.5 * cnv_w + 0.5f * top_x * cnv_w, get_scaled_total_height(), cnv_h); } } @@ -1546,15 +1185,16 @@ GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& g return GLGizmosManager::EType::Undefined; } -bool GLGizmosManager::generate_icons_texture() const +bool GLGizmosManager::generate_icons_texture() { std::string path = resources_dir() + "/images/"; std::vector filenames; for (size_t idx=0; idxget_icon_filename(); + const std::string& icon_filename = gizmo->get_icon_filename(); if (!icon_filename.empty()) filenames.push_back(path + icon_filename); } @@ -1580,76 +1220,68 @@ bool GLGizmosManager::generate_icons_texture() const return res; } -void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) +void GLGizmosManager::update_hover_state(const EType &type) { - if (!m_enabled) + assert(m_enabled); + if (type == Undefined) { + m_hover = Undefined; + m_tooltip.clear(); return; - - size_t idx = get_gizmo_idx_from_mouse(mouse_pos); - if (idx != Undefined && m_gizmos[idx]->is_activable() && m_hover == idx) { - activate_gizmo(m_current == idx ? Undefined : (EType)idx); - // BBS - wxGetApp().obj_list()->select_object_item((EType) idx <= Scale || (EType) idx == Text); - } -} - -std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) -{ - std::string name = ""; - - if (!m_enabled) - return name; - - m_hover = Undefined; - - size_t idx = get_gizmo_idx_from_mouse(mouse_pos); - if (idx != Undefined) { - name = m_gizmos[idx]->get_name(); - - if (m_gizmos[idx]->is_activable()) - m_hover = (EType)idx; } - return name; + const GLGizmoBase &hovered_gizmo = *m_gizmos[type]; + m_hover = hovered_gizmo.is_activable() ? type : Undefined; + m_tooltip = hovered_gizmo.get_name(); } bool GLGizmosManager::activate_gizmo(EType type) { - if (m_gizmos.empty() || m_current == type) - return true; + assert(!m_gizmos.empty()); - GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get(); - GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get(); + // already activated + if (m_current == type) return true; - if (old_gizmo) { - //if (m_current == Text) { - // wxGetApp().imgui()->destroy_fonts_texture(); - //} - old_gizmo->set_state(GLGizmoBase::Off); - if (old_gizmo->get_state() != GLGizmoBase::Off) + if (m_current != Undefined) { + // clean up previous gizmo + GLGizmoBase &old_gizmo = *m_gizmos[m_current]; + old_gizmo.set_state(GLGizmoBase::Off); + if (old_gizmo.get_state() != GLGizmoBase::Off) return false; // gizmo refused to be turned off, do nothing. - if (! m_parent.get_gizmos_manager().is_serializing() - && old_gizmo->wants_enter_leave_snapshots()) - Plater::TakeSnapshot snapshot(wxGetApp().plater(), - old_gizmo->get_gizmo_leaving_text(), - UndoRedo::SnapshotType::LeavingGizmoWithAction); + old_gizmo.unregister_raycasters_for_picking(); + + if (!m_serializing && old_gizmo.wants_enter_leave_snapshots()) + Plater::TakeSnapshot + snapshot(wxGetApp().plater(), + old_gizmo.get_gizmo_leaving_text(), + UndoRedo::SnapshotType::LeavingGizmoWithAction); } - if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing() - && new_gizmo->wants_enter_leave_snapshots()) + if (type == Undefined) { + // it is deactivation of gizmo + m_current = Undefined; + return true; + } + + // set up new gizmo + GLGizmoBase& new_gizmo = *m_gizmos[type]; + if (!new_gizmo.is_activable()) return false; + + if (!m_serializing && new_gizmo.wants_enter_leave_snapshots()) Plater::TakeSnapshot snapshot(wxGetApp().plater(), - new_gizmo->get_gizmo_entering_text(), - UndoRedo::SnapshotType::EnteringGizmo); + new_gizmo.get_gizmo_entering_text(), + UndoRedo::SnapshotType::EnteringGizmo); m_current = type; - - if (new_gizmo) { - //if (m_current == Text) { - // wxGetApp().imgui()->load_fonts_texture(); - //} - new_gizmo->set_state(GLGizmoBase::On); + new_gizmo.set_state(GLGizmoBase::On); + if (new_gizmo.get_state() != GLGizmoBase::On) { + m_current = Undefined; + return false; // gizmo refused to be turned on. } + + new_gizmo.register_raycasters_for_picking(); + + // sucessful activation of gizmo return true; } @@ -1666,11 +1298,11 @@ bool GLGizmosManager::grabber_contains_mouse() const bool GLGizmosManager::is_in_editing_mode(bool error_notification) const { - if (m_current != SlaSupports || ! dynamic_cast(get_current())->is_in_editing_mode()) + //if (m_current != SlaSupports || ! dynamic_cast(get_current())->is_in_editing_mode()) return false; // BBS: remove SLA editing notification - return true; + //return true; } @@ -1681,12 +1313,6 @@ bool GLGizmosManager::is_hiding_instances() const && m_common_gizmos_data->instances_hider()->is_valid()); } - -int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const -{ - return m_gizmos[type]->get_shortcut_key(); -} - std::string get_name_from_gizmo_etype(GLGizmosManager::EType type) { switch (type) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index fefb85b6b9..02169e08d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GUI_GLGizmosManager_hpp_ #define slic3r_GUI_GLGizmosManager_hpp_ @@ -31,16 +35,24 @@ class CommonGizmosDataPool; class GizmoObjectManipulation; class Rect { - float m_left; - float m_top; - float m_right; - float m_bottom; + float m_left{ 0.0f }; + float m_top{ 0.0f }; + float m_right{ 0.0f }; + float m_bottom{ 0.0f }; public: - Rect() : m_left(0.0f) , m_top(0.0f) , m_right(0.0f) , m_bottom(0.0f) {} - + Rect() = default; Rect(float left, float top, float right, float bottom) : m_left(left) , m_top(top) , m_right(right) , m_bottom(bottom) {} + bool operator == (const Rect& other) const { + if (std::abs(m_left - other.m_left) > EPSILON) return false; + if (std::abs(m_top - other.m_top) > EPSILON) return false; + if (std::abs(m_right - other.m_right) > EPSILON) return false; + if (std::abs(m_bottom - other.m_bottom) > EPSILON) return false; + return true; + } + bool operator != (const Rect& other) const { return !operator==(other); } + float get_left() const { return m_left; } void set_left(float left) { m_left = left; } @@ -76,11 +88,12 @@ public: // BBS Text, MmuSegmentation, + Measure, Simplify, - SlaSupports, + //SlaSupports, // BBS //FaceRecognition, - Hollow, + //Hollow, Undefined, }; @@ -108,10 +121,10 @@ private: GLCanvas3D& m_parent; bool m_enabled; std::vector> m_gizmos; - mutable GLTexture m_icons_texture; - mutable bool m_icons_texture_dirty; + GLTexture m_icons_texture; + bool m_icons_texture_dirty; BackgroundTexture m_background_texture; - BackgroundTexture m_arrow_texture; + GLTexture m_arrow_texture; Layout m_layout; EType m_current; EType m_hover; @@ -121,24 +134,10 @@ private: GizmoObjectManipulation m_object_manipulation; std::vector get_selectable_idxs() const; - size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; + EType get_gizmo_from_mouse(const Vec2d &mouse_pos) const; bool activate_gizmo(EType type); - struct MouseCapture - { - bool left; - bool middle; - bool right; - GLCanvas3D* parent; - - MouseCapture() { reset(); } - - bool any() const { return left || middle || right; } - void reset() { left = middle = right = false; parent = nullptr; } - }; - - MouseCapture m_mouse_capture; std::string m_tooltip; bool m_serializing; std::unique_ptr m_common_gizmos_data; @@ -147,6 +146,15 @@ private: std::map icon_list; bool m_is_dark = false; + + /// + /// Process mouse event on gizmo toolbar + /// + /// Event descriptor + /// TRUE when take responsibility for event otherwise FALSE. + /// On true, event should not be process by others. + /// On false, event should be process by others. + bool gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event); public: std::unique_ptr m_assemble_view_data; @@ -172,7 +180,7 @@ public: float get_layout_scale(); - bool init_arrow(const BackgroundTexture::Metadata& arrow_texture); + bool init_arrow(const std::string& filename); template void load(Archive& ar) @@ -218,14 +226,15 @@ public: void refresh_on_off_state(); void reset_all_states(); - bool is_serializing() const { return m_serializing; } bool open_gizmo(EType type); bool check_gizmos_closed_except(EType) const; void set_hover_id(int id); - void enable_grabber(EType type, unsigned int id, bool enable); - void update(const Linef3& mouse_ray, const Point& mouse_pos); + /// + /// Distribute information about different data into active gizmo + /// Should be called when selection changed + /// void update_data(); void update_assemble_view_data(); @@ -238,21 +247,6 @@ public: bool handle_shortcut(int key); bool is_dragging() const; - void start_dragging(); - void stop_dragging(); - - Vec3d get_displacement() const; - - Vec3d get_scale() const; - void set_scale(const Vec3d& scale); - - Vec3d get_scale_offset() const; - - Vec3d get_rotation() const; - void set_rotation(const Vec3d& rotation); - - // BBS - void finish_cut_rotation(); //BBS void* get_icon_texture_id(MENU_ICON_NAME icon) { @@ -268,15 +262,6 @@ public: return nullptr; } - Vec3d get_flattening_normal() const; - - void set_flattening_data(const ModelObject* model_object); - - void set_sla_support_data(ModelObject* model_object); - - void set_painter_gizmo_data(); - - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); ClippingPlane get_clipping_plane() const; ClippingPlane get_assemble_view_clipping_plane() const; bool wants_reslice_supports_on_undo() const; @@ -286,8 +271,7 @@ public: void on_change_color_mode(bool is_dark); void render_current_gizmo() const; - void render_current_gizmo_for_picking_pass() const; - void render_painter_gizmo() const; + void render_painter_gizmo(); void render_painter_assemble_view() const; void render_overlay(); @@ -296,15 +280,14 @@ public: std::string get_tooltip() const; - bool on_mouse(wxMouseEvent& evt); - bool on_mouse_wheel(wxMouseEvent& evt); + bool on_mouse(const wxMouseEvent &mouse_event); + bool on_mouse_wheel(const wxMouseEvent &evt); bool on_char(wxKeyEvent& evt); bool on_key(wxKeyEvent& evt); void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); } - int get_shortcut_key(GLGizmosManager::EType) const; // To end highlight set gizmo = undefined void set_highlight(EType gizmo, bool highlight_shown) { m_highlight = std::pair(gizmo, highlight_shown); } @@ -317,14 +300,19 @@ public: bool get_uniform_scaling() const { return m_object_manipulation.get_uniform_scaling();} private: - void render_background(float left, float top, float right, float bottom, float border) const; + bool gizmo_event(SLAGizmoEventType action, + const Vec2d & mouse_position = Vec2d::Zero(), + bool shift_down = false, + bool alt_down = false, + bool control_down = false); + + void render_background(float left, float top, float right, float bottom, float border_w, float border_h) const; void do_render_overlay() const; - bool generate_icons_texture() const; + bool generate_icons_texture(); - void update_on_off_state(const Vec2d& mouse_pos); - std::string update_hover_state(const Vec2d& mouse_pos); + void update_hover_state(const EType &type); bool grabber_contains_mouse() const; }; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 2cdd8aac36..1a5b891598 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -86,7 +86,7 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) ObjectList* obj_list = wxGetApp().obj_list(); if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* volume = selection.get_first_volume(); m_new_position = volume->get_instance_offset(); if (m_world_coordinates) { @@ -118,7 +118,7 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) } else if (selection.is_single_modifier() || selection.is_single_volume()) { // the selection contains a single volume - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* volume = selection.get_first_volume(); m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation() * (180. / M_PI); m_new_scale = volume->get_volume_scaling_factor() * 100.; @@ -217,7 +217,7 @@ void GizmoObjectManipulation::update_reset_buttons_visibility() const Selection& selection = m_glcanvas.get_selection(); if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* volume = selection.get_first_volume(); Vec3d rotation; Vec3d scale; double min_z = 0.; @@ -259,7 +259,7 @@ void GizmoObjectManipulation::change_position_value(int axis, double value) position(axis) = value; Selection& selection = m_glcanvas.get_selection(); - selection.start_dragging(); + selection.setup_cache(); selection.translate(position - m_cache.position, selection.requires_local_axes()); m_glcanvas.do_move(L("Set Position")); @@ -287,7 +287,7 @@ void GizmoObjectManipulation::change_rotation_value(int axis, double value) transformation_type.set_local(); } - selection.start_dragging(); + selection.setup_cache(); selection.rotate( (M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation), transformation_type); @@ -331,14 +331,14 @@ void GizmoObjectManipulation::change_size_value(int axis, double value) Vec3d ref_size = m_cache.size; if (selection.is_single_volume() || selection.is_single_modifier()) { - Vec3d instance_scale = wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->instances[0]->get_transformation().get_scaling_factor(); - ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box().size(); + Vec3d instance_scale = wxGetApp().model().objects[selection.get_first_volume()->object_idx()]->instances[0]->get_transformation().get_scaling_factor(); + ref_size = selection.get_first_volume()->bounding_box().size(); ref_size = Vec3d(instance_scale[0] * ref_size[0], instance_scale[1] * ref_size[1], instance_scale[2] * ref_size[2]); } else if (selection.is_single_full_instance()) ref_size = m_world_coordinates ? selection.get_unscaled_instance_bounding_box().size() : - wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size(); + wxGetApp().model().objects[selection.get_first_volume()->object_idx()]->raw_mesh_bounding_box().size(); this->do_scale(axis, 100. * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); @@ -363,7 +363,7 @@ void GizmoObjectManipulation::do_scale(int axis, const Vec3d &scale) const if (m_uniform_scale/* || selection.requires_uniform_scale()*/) scaling_factor = scale(axis) * Vec3d::Ones(); - selection.start_dragging(); + selection.setup_cache(); selection.scale(scaling_factor * 0.01, transformation_type); m_glcanvas.do_scale(L("Set Scale")); } @@ -391,7 +391,7 @@ void GizmoObjectManipulation::reset_position_value() Selection& selection = m_glcanvas.get_selection(); if (selection.is_single_volume() || selection.is_single_modifier()) { - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + GLVolume* volume = const_cast(selection.get_first_volume()); volume->set_volume_offset(Vec3d::Zero()); } else if (selection.is_single_full_instance()) { @@ -414,7 +414,7 @@ void GizmoObjectManipulation::reset_rotation_value() Selection& selection = m_glcanvas.get_selection(); if (selection.is_single_volume() || selection.is_single_modifier()) { - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + GLVolume* volume = const_cast(selection.get_first_volume()); volume->set_volume_rotation(Vec3d::Zero()); } else if (selection.is_single_full_instance()) { @@ -450,7 +450,7 @@ void GizmoObjectManipulation::set_uniform_scaling(const bool new_value) if (selection.is_single_full_instance() && m_world_coordinates && !new_value) { // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* volume = selection.get_first_volume(); // Is the angle close to a multiple of 90 degrees? if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) { diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index 0c8094a4bb..3f4dbd00c0 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -586,8 +586,8 @@ void IMSlider::draw_colored_band(const ImRect& groove, const ImRect& slideable_r }; //draw main colored band const int default_color_idx = m_mode == MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; - std::arrayrgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]); - ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + ColorRGBA rgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]); + ImU32 band_clr = ImGuiWrapper::to_ImU32(rgba); draw_main_band(band_clr); static float tick_pos; @@ -605,8 +605,8 @@ void IMSlider::draw_colored_band(const ImRect& groove, const ImRect& slideable_r const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it); if (!clr_str.empty()) { - std::arrayrgba = decode_color_to_float_array(clr_str); - ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + ColorRGBA rgba = decode_color_to_float_array(clr_str); + ImU32 band_clr = ImGuiWrapper::to_ImU32(rgba); if (tick_it->tick == 0) draw_main_band(band_clr); else @@ -1323,9 +1323,9 @@ void IMSlider::render_add_menu() } else if (begin_menu(_u8L("Change Filament").c_str())) { for (int i = 0; i < extruder_num; i++) { - std::array rgba = decode_color_to_float_array(m_extruder_colors[i]); - ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); - if (rgba[3] == 0) + ColorRGBA rgba = decode_color_to_float_array(m_extruder_colors[i]); + ImU32 icon_clr = ImGuiWrapper::to_ImU32(rgba); + if (rgba.a() == 0) icon_clr = 0; if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr, false, true, &hovered)) add_code_as_tick(ToolChange, i + 1); if (hovered) { show_tooltip(_u8L("Change filament at the beginning of this layer.")); } @@ -1373,8 +1373,8 @@ void IMSlider::render_edit_menu(const TickCode& tick) } else if (begin_menu(_u8L("Change Filament").c_str())) { for (int i = 0; i < extruder_num; i++) { - std::array rgba = decode_color_to_float_array(m_extruder_colors[i]); - ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + ColorRGBA rgba = decode_color_to_float_array(m_extruder_colors[i]); + ImU32 icon_clr = ImGuiWrapper::to_ImU32(rgba); if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1); } end_menu(); diff --git a/src/slic3r/GUI/IMSlider_Utils.hpp b/src/slic3r/GUI/IMSlider_Utils.hpp deleted file mode 100644 index bfe5545a3b..0000000000 --- a/src/slic3r/GUI/IMSlider_Utils.hpp +++ /dev/null @@ -1,198 +0,0 @@ -#ifndef slic3r_IMSlider_Utils_hpp_ -#define slic3r_IMSlider_Utils_hpp_ - -#include -#include - -#include "wx/colour.h" - -//double epsilon() { return 0.0011; } - -class ColorGenerator -{ - // Some of next code is borrowed from https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both - typedef struct { - double r; // a fraction between 0 and 1 - double g; // a fraction between 0 and 1 - double b; // a fraction between 0 and 1 - } rgb; - - typedef struct { - double h; // angle in degrees - double s; // a fraction between 0 and 1 - double v; // a fraction between 0 and 1 - } hsv; - - //static hsv rgb2hsv(rgb in); - //static rgb hsv2rgb(hsv in); - - hsv rgb2hsv(rgb in) - { - hsv out; - double min, max, delta; - - min = in.r < in.g ? in.r : in.g; - min = min < in.b ? min : in.b; - - max = in.r > in.g ? in.r : in.g; - max = max > in.b ? max : in.b; - - out.v = max; // v - delta = max - min; - if (delta < 0.00001) - { - out.s = 0; - out.h = 0; // undefined, maybe nan? - return out; - } - if (max > 0.0) { // NOTE: if Max is == 0, this divide would cause a crash - out.s = (delta / max); // s - } - else { - // if max is 0, then r = g = b = 0 - // s = 0, h is undefined - out.s = 0.0; - out.h = NAN; // its now undefined - return out; - } - if (in.r >= max) // > is bogus, just keeps compilor happy - out.h = (in.g - in.b) / delta; // between yellow & magenta - else - if (in.g >= max) - out.h = 2.0 + (in.b - in.r) / delta; // between cyan & yellow - else - out.h = 4.0 + (in.r - in.g) / delta; // between magenta & cyan - - out.h *= 60.0; // degrees - - if (out.h < 0.0) - out.h += 360.0; - - return out; - } - - hsv rgb2hsv(const std::string& str_clr_in) - { - wxColour clr(str_clr_in); - rgb in = { clr.Red() / 255.0, clr.Green() / 255.0, clr.Blue() / 255.0 }; - return rgb2hsv(in); - } - - - rgb hsv2rgb(hsv in) - { - double hh, p, q, t, ff; - long i; - rgb out; - - if (in.s <= 0.0) { // < is bogus, just shuts up warnings - out.r = in.v; - out.g = in.v; - out.b = in.v; - return out; - } - hh = in.h; - if (hh >= 360.0) hh -= 360.0;//hh = 0.0; - hh /= 60.0; - i = (long)hh; - ff = hh - i; - p = in.v * (1.0 - in.s); - q = in.v * (1.0 - (in.s * ff)); - t = in.v * (1.0 - (in.s * (1.0 - ff))); - - switch (i) { - case 0: - out.r = in.v; - out.g = t; - out.b = p; - break; - case 1: - out.r = q; - out.g = in.v; - out.b = p; - break; - case 2: - out.r = p; - out.g = in.v; - out.b = t; - break; - - case 3: - out.r = p; - out.g = q; - out.b = in.v; - break; - case 4: - out.r = t; - out.g = p; - out.b = in.v; - break; - case 5: - default: - out.r = in.v; - out.g = p; - out.b = q; - break; - } - return out; - } - - std::random_device rd; - -public: - - ColorGenerator() {} - ~ColorGenerator() {} - - double rand_val() - { - std::mt19937 rand_generator(rd()); - - // this value will be used for Saturation and Value - // to avoid extremely light/dark colors, take this value from range [0.65; 1.0] - std::uniform_real_distribution distrib(0.65, 1.0); - return distrib(rand_generator); - } - - - std::string get_opposite_color(const std::string& color) - { - std::string opp_color = ""; - - hsv hsv_clr = rgb2hsv(color); - hsv_clr.h += 65; // 65 instead 60 to avoid circle values - hsv_clr.s = rand_val(); - hsv_clr.v = rand_val(); - - rgb rgb_opp_color = hsv2rgb(hsv_clr); - - wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255)); - opp_color = clr_str.ToStdString(); - - return opp_color; - } - - std::string get_opposite_color(const std::string& color_frst, const std::string& color_scnd) - { - std::string opp_color = ""; - - hsv hsv_frst = rgb2hsv(color_frst); - hsv hsv_scnd = rgb2hsv(color_scnd); - - double delta_h = fabs(hsv_frst.h - hsv_scnd.h); - double start_h = delta_h > 180 ? std::min(hsv_scnd.h, hsv_frst.h) : std::max(hsv_scnd.h, hsv_frst.h); - start_h += 5; // to avoid circle change of colors for 120 deg - if (delta_h < 180) - delta_h = 360 - delta_h; - - hsv hsv_opp = hsv{ start_h + 0.5 * delta_h, rand_val(), rand_val() }; - rgb rgb_opp_color = hsv2rgb(hsv_opp); - - wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255)); - opp_color = clr_str.ToStdString(); - - return opp_color; - } -}; - -#endif \ No newline at end of file diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 5aac115586..d56a870e68 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1,3 +1,8 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "ImGuiWrapper.hpp" #include @@ -27,12 +32,15 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" #include "libslic3r/Shape/TextShape.hpp" + #include "3DScene.hpp" #include "GUI.hpp" #include "I18N.hpp" #include "Search.hpp" #include "BitmapCache.hpp" +#include "GUI_App.hpp" #include "../Utils/MacDarkMode.hpp" #include @@ -59,6 +67,7 @@ static const std::map font_icons = { #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT {ImGui::SliderFloatEditBtnIcon, "edit_button" }, #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT + {ImGui::ClipboardBtnIcon , "copy_menu" }, {ImGui::CircleButtonIcon , "circle_paint" }, {ImGui::TriangleButtonIcon , "triangle_paint" }, {ImGui::FillButtonIcon , "fill_paint" }, @@ -75,6 +84,7 @@ static const std::map font_icons = { {ImGui::PreferencesDarkButton , "notification_preferences_dark" }, {ImGui::PreferencesHoverDarkButton , "notification_preferences_hover_dark"}, + {ImGui::ClipboardBtnDarkIcon , "copy_menu_dark" }, {ImGui::CircleButtonDarkIcon , "circle_paint_dark" }, {ImGui::TriangleButtonDarkIcon , "triangle_paint_dark" }, {ImGui::FillButtonDarkIcon , "fill_paint_dark" }, @@ -127,11 +137,11 @@ static const std::map font_icons_extra_large = { const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f }; -const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f }; +const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = to_ImVec4(ColorRGBA::ORANGE()); const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.1f, 0.1f, 0.1f, 0.8f }; const ImVec4 ImGuiWrapper::COL_BUTTON_BACKGROUND = COL_ORANGE_DARK; const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT; -const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED; +const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = COL_BUTTON_HOVERED; //BBS @@ -144,6 +154,7 @@ const ImVec4 ImGuiWrapper::COL_SEPARATOR_DARK = { 0.24f, 0.24f, 0.27f, 1.0f } const ImVec4 ImGuiWrapper::COL_TITLE_BG = { 0.745f, 0.745f, 0.745f, 1.0f }; const ImVec4 ImGuiWrapper::COL_WINDOW_BG = { 1.000f, 1.000f, 1.000f, 1.0f }; const ImVec4 ImGuiWrapper::COL_WINDOW_BG_DARK = { 45 / 255.f, 45 / 255.f, 49 / 255.f, 1.f }; +const ImVec4 ImGuiWrapper::COL_ORCA = to_ImVec4(ColorRGBA::ORCA()); int ImGuiWrapper::TOOLBAR_WINDOW_FLAGS = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove @@ -499,10 +510,12 @@ void ImGuiWrapper::render() m_new_frame_open = false; } -ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const +ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, + bool hide_text_after_double_hash, + float wrap_width) const { auto text_utf8 = into_u8(text); - ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width); + ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, hide_text_after_double_hash, wrap_width); /*#ifdef __linux__ size.x *= m_style_scaling; @@ -761,16 +774,30 @@ void ImGuiWrapper::end() ImGui::End(); } -bool ImGuiWrapper::button(const wxString &label) +bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip) { auto label_utf8 = into_u8(label); - return ImGui::Button(label_utf8.c_str()); + const bool ret = ImGui::Button(label_utf8.c_str()); + + if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) { + auto tooltip_utf8 = into_u8(tooltip); + ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr); + } + + return ret; } -bool ImGuiWrapper::bbl_button(const wxString &label) +bool ImGuiWrapper::bbl_button(const wxString &label, const wxString& tooltip) { auto label_utf8 = into_u8(label); - return ImGui::BBLButton(label_utf8.c_str()); + const bool ret = ImGui::BBLButton(label_utf8.c_str()); + + if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) { + auto tooltip_utf8 = into_u8(tooltip); + ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr); + } + + return ret; } bool ImGuiWrapper::button(const wxString& label, float width, float height) @@ -779,17 +806,23 @@ bool ImGuiWrapper::button(const wxString& label, float width, float height) return ImGui::Button(label_utf8.c_str(), ImVec2(width, height)); } +bool ImGuiWrapper::button(const wxString& label, const ImVec2 &size, bool enable) +{ + disabled_begin(!enable); + + auto label_utf8 = into_u8(label); + bool res = ImGui::Button(label_utf8.c_str(), size); + + disabled_end(); + return (enable) ? res : false; +} + bool ImGuiWrapper::radio_button(const wxString &label, bool active) { auto label_utf8 = into_u8(label); return ImGui::RadioButton(label_utf8.c_str(), active); } -bool ImGuiWrapper::image_button() -{ - return false; -} - bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) { return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); @@ -958,6 +991,8 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float m_last_slider_status.edited = ImGui::IsItemEdited(); m_last_slider_status.clicked = ImGui::IsItemClicked(); m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit(); + if (!m_last_slider_status.can_take_snapshot) + m_last_slider_status.can_take_snapshot = ImGui::IsItemClicked(); if (!tooltip.empty() && ImGui::IsItemHovered()) this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width); @@ -1031,25 +1066,99 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl } #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT -bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection) +static bool image_button_ex(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + ImGui::ItemSize(bb); + if (!ImGui::ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImGui::RenderNavHighlight(bb, id); + ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui::GetColorU32(bg_col)); + window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui::GetColorU32(tint_col)); + + return pressed; +} + +bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + ImGui::PushID((void*)(intptr_t)user_texture_id); + const ImGuiID id = window->GetID("#image"); + ImGui::PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding; + return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); +} + +bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip) +{ + const ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); + const bool res = image_button(tex_id, size, uv0, uv1); + ImGui::PopStyleColor(3); + + if (!tooltip.empty() && ImGui::IsItemHovered()) + this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + + return res; +} + +bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/) +{ + return combo(into_u8(label), options, selection, flags, label_width, item_width); +} + +bool ImGuiWrapper::combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/) { // this is to force the label to the left of the widget: - text(label); - ImGui::SameLine(); + const bool hidden_label = boost::starts_with(label, "##"); + if (!label.empty() && !hidden_label) { + text(label); + ImGui::SameLine(label_width); + } + ImGui::PushItemWidth(item_width); int selection_out = selection; bool res = false; const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : ""; - if (ImGui::BeginCombo("", selection_str)) { + if (ImGui::BeginCombo(hidden_label ? label.c_str() : ("##" + label).c_str(), selection_str, flags)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { selection_out = i; + res = true; } } ImGui::EndCombo(); - res = true; } selection = selection_out; @@ -1735,6 +1844,20 @@ void ImGuiWrapper::title(const std::string& str) ImGui::Separator(); } +void ImGuiWrapper::title(const std::string &str, bool suppress_seperator) +{ + if (bold_font) { + ImGui::PushFont(bold_font); + text(str); + ImGui::PopFont(); + } else { + text(str); + } + if (!suppress_seperator) { + ImGui::Separator(); + } +} + void ImGuiWrapper::disabled_begin(bool disabled) { wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call"); @@ -1776,6 +1899,37 @@ bool ImGuiWrapper::want_any_input() const return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; } +ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) +{ + auto item = m_custom_glyph_rects_ids.find(tex_id); + return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr; +} + +void ImGuiWrapper::disable_background_fadeout_animation() +{ + GImGui->DimBgRatio = 1.0f; +} + +ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) +{ + return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); +} + +ImVec4 ImGuiWrapper::to_ImVec4(const ColorRGBA& color) +{ + return { color.r(), color.g(), color.b(), color.a() }; +} + +ColorRGBA ImGuiWrapper::from_ImU32(const ImU32& color) +{ + return from_ImVec4(ImGui::ColorConvertU32ToFloat4(color)); +} + +ColorRGBA ImGuiWrapper::from_ImVec4(const ImVec4& color) +{ + return { color.x, color.y, color.z, color.w }; +} + #ifdef __APPLE__ static const ImWchar ranges_keyboard_shortcuts[] = { @@ -2127,12 +2281,18 @@ void ImGuiWrapper::init_font(bool compress) int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next // add rectangles for the icons to the font atlas - for (auto& icon : font_icons) + for (auto& icon : font_icons) { + m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); - for (auto& icon : font_icons_large) + } + for (auto& icon : font_icons_large) { + m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); - for (auto& icon : font_icons_extra_large) + } + for (auto& icon : font_icons_extra_large) { + m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); + } // Build texture atlas unsigned char* pixels; @@ -2140,55 +2300,37 @@ void ImGuiWrapper::init_font(bool compress) io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. BOOST_LOG_TRIVIAL(trace) << "Build default font texture done. width: " << width << ", height: " << height; - // Fill rectangles from the SVG-icons - for (auto icon : font_icons) { + auto load_icon_from_svg = [this, &io, pixels, width, &rect_id](const std::pair icon, int icon_sz) { if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { assert(rect->Width == icon_sz); assert(rect->Height == icon_sz); - unsigned outwidth, outheight; + unsigned outwidth, outheight; std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight); - const ImU32* pIn = (ImU32*)raw_data.data(); - for (unsigned y = 0; y < outheight; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (unsigned x = 0; x < outwidth; x++) - *pOut++ = *pIn++; + if (!raw_data.empty()) { + const ImU32* pIn = (ImU32*)raw_data.data(); + for (unsigned y = 0; y < outheight; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (unsigned x = 0; x < outwidth; x++) + *pOut++ = *pIn++; + } } } rect_id++; + }; + + // Fill rectangles from the SVG-icons + for (auto icon : font_icons) { + load_icon_from_svg(icon, icon_sz); } icon_sz *= 2; // default size of large icon is 32 px for (auto icon : font_icons_large) { - if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { - assert(rect->Width == icon_sz); - assert(rect->Height == icon_sz); - unsigned outwidth, outheight; - std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight); - const ImU32* pIn = (ImU32*)raw_data.data(); - for (unsigned y = 0; y < outheight; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (unsigned x = 0; x < outwidth; x++) - *pOut++ = *pIn++; - } - } - rect_id++; + load_icon_from_svg(icon, icon_sz); } icon_sz *= 2; // default size of extra large icon is 64 px for (auto icon : font_icons_extra_large) { - if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { - assert(rect->Width == icon_sz); - assert(rect->Height == icon_sz); - unsigned outwidth, outheight; - std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight); - const ImU32* pIn = (ImU32*)raw_data.data(); - for (unsigned y = 0; y < outheight; y++) { - ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); - for (unsigned x = 0; x < outwidth; x++) - *pOut++ = *pIn++; - } - } - rect_id++; + load_icon_from_svg(icon, icon_sz); } // Upload texture to graphics system @@ -2365,100 +2507,147 @@ void ImGuiWrapper::init_style() void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) { + if (draw_data == nullptr || draw_data->CmdListsCount == 0) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("imgui"); + if (shader == nullptr) + return; + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + const int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + const int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); if (fb_width == 0 || fb_height == 0) return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); + + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + shader->start_using(); // We are using the OpenGL fixed pipeline to make the example code simpler to read! // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); - GLint last_polygon_mode[2]; glsafe(::glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode)); - GLint last_viewport[4]; glsafe(::glGetIntegerv(GL_VIEWPORT, last_viewport)); - GLint last_scissor_box[4]; glsafe(::glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box)); + GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + GLint last_polygon_mode[2]; glsafe(::glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode)); + GLint last_viewport[4]; glsafe(::glGetIntegerv(GL_VIEWPORT, last_viewport)); + GLint last_scissor_box[4]; glsafe(::glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box)); + GLint last_texture_env_mode; glsafe(::glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &last_texture_env_mode)); glsafe(::glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_LIGHTING)); - glsafe(::glDisable(GL_COLOR_MATERIAL)); glsafe(::glEnable(GL_SCISSOR_TEST)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - glsafe(::glEnableClientState(GL_COLOR_ARRAY)); glsafe(::glEnable(GL_TEXTURE_2D)); glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); - GLint texture_env_mode = GL_MODULATE; - glsafe(::glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &texture_env_mode)); - glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. glsafe(::glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); - glsafe(::glMatrixMode(GL_PROJECTION)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - glsafe(::glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f)); - glsafe(::glMatrixMode(GL_MODELVIEW)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); + const float L = draw_data->DisplayPos.x; + const float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + const float T = draw_data->DisplayPos.y; + const float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + + Matrix4f ortho_projection; + ortho_projection << + 2.0f / (R - L), 0.0f, 0.0f, (R + L) / (L - R), + 0.0f, 2.0f / (T - B), 0.0f, (T + B) / (B - T), + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f; + + shader->set_uniform("Texture", 0); + shader->set_uniform("ProjMtx", ortho_projection); + + // Will project scissor/clipping rectangles into framebuffer space + const ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + const ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) // Render command lists - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { + for (int n = 0; n < draw_data->CmdListsCount; ++n) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glsafe(::glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)))); - glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)))); - glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)))); + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); + const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { + GLuint vbo_id; + glsafe(::glGenBuffers(1, &vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, vtx_buffer, GL_STATIC_DRAW)); + + GLuint ibo_id; + glsafe(::glGenBuffers(1, &ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, idx_buffer, GL_STATIC_DRAW)); + + const int position_id = shader->get_attrib_location("Position"); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (const void*)IM_OFFSETOF(ImDrawVert, pos))); + glsafe(::glEnableVertexAttribArray(position_id)); + } + const int uv_id = shader->get_attrib_location("UV"); + if (uv_id != -1) { + glsafe(::glVertexAttribPointer(uv_id, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (const void*)IM_OFFSETOF(ImDrawVert, uv))); + glsafe(::glEnableVertexAttribArray(uv_id)); + } + const int color_id = shader->get_attrib_location("Color"); + if (color_id != -1) { + glsafe(::glVertexAttribPointer(color_id, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (const void*)IM_OFFSETOF(ImDrawVert, col))); + glsafe(::glEnableVertexAttribArray(color_id)); + } + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; ++cmd_i) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) - { // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); - } - else - { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y))); + else { + // Project scissor/clipping rectangles into framebuffer space + const ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + const ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; - // Bind texture, Draw - glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId)); - glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer)); - } + // Apply scissor/clipping rectangle (Y is inverted in OpenGL) + glsafe(::glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); + + // Bind texture, Draw + glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); + glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); } - idx_buffer += pcmd->ElemCount; } + + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + if (uv_id != -1) + glsafe(::glDisableVertexAttribArray(uv_id)); + if (color_id != -1) + glsafe(::glDisableVertexAttribArray(color_id)); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + glsafe(::glDeleteBuffers(1, &ibo_id)); + glsafe(::glDeleteBuffers(1, &vbo_id)); } // Restore modified state - glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texture_env_mode)); - glsafe(::glDisableClientState(GL_COLOR_ARRAY)); - glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_texture_env_mode)); glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture)); - glsafe(::glMatrixMode(GL_MODELVIEW)); - glsafe(::glPopMatrix()); - glsafe(::glMatrixMode(GL_PROJECTION)); - glsafe(::glPopMatrix()); glsafe(::glPopAttrib()); - glsafe(::glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1])); + glsafe(::glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); + glsafe(::glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]))); glsafe(::glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3])); glsafe(::glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3])); + + shader->stop_using(); + + if (curr_shader != nullptr) + curr_shader->start_using(); } bool ImGuiWrapper::display_initialized() const diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 052ea00e5e..58e42f1996 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_ImGuiWrapper_hpp_ #define slic3r_ImGuiWrapper_hpp_ @@ -11,9 +15,12 @@ #include "libslic3r/Point.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" -namespace Slic3r {namespace Search { +namespace Slic3r { +class ColorRGBA; +namespace Search { struct OptionViewParameters; -}} +} // namespace Search +} // namespace Slic3r class wxString; class wxMouseEvent; @@ -58,6 +65,7 @@ class ImGuiWrapper #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT bool m_requires_extra_frame{ false }; #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT + std::map m_custom_glyph_rects_ids; std::string m_clipboard_text; public: @@ -66,6 +74,11 @@ public: bool edited { false }; bool clicked { false }; bool deactivated_after_edit { false }; + // flag to indicate possibility to take snapshot from the slider value + // It's used from Gizmos to take snapshots just from the very beginning of the editing + bool can_take_snapshot { false }; + // When Undo/Redo snapshot is taken, then call this function + void invalidate_snapshot() { can_take_snapshot = false; } }; ImGuiWrapper(); @@ -87,12 +100,13 @@ public: float scaled(float x) const { return x * m_font_size; } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } - ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const; + ImVec2 calc_text_size(const wxString &text, bool hide_text_after_double_hash = false, float wrap_width = -1.0f) const; ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const; ImVec2 get_item_spacing() const; float get_slider_float_height() const; const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } + LastSliderStatus& get_last_slider_status() { return m_last_slider_status; } void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); @@ -110,11 +124,11 @@ public: bool begin(const wxString& name, bool* close, int flags = 0); void end(); - bool button(const wxString &label); - bool bbl_button(const wxString &label); - bool button(const wxString& label, float width, float height); + bool button(const wxString &label, const wxString& tooltip = {}); + bool bbl_button(const wxString &label, const wxString& tooltip = {}); + bool button(const wxString& label, float width, float height); + bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f) bool radio_button(const wxString &label, bool active); - bool image_button(); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); @@ -147,7 +161,12 @@ public: bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true); #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT - bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected + bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + bool image_button(const wchar_t icon, const wxString& tooltip = L""); + + // Use selection = -1 to not mark any option as selected + bool combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f); + bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f); bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, Search::OptionViewParameters &view_params, @@ -157,6 +176,7 @@ public: bool is_localized); void bold_text(const std::string &str); void title(const std::string& str); + void title(const std::string &str, bool suppress_seperator); // set font const std::vector get_fonts_names() const { return m_fonts_names; } @@ -181,6 +201,15 @@ public: void reset_requires_extra_frame() { m_requires_extra_frame = false; } #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT + void disable_background_fadeout_animation(); + + static ImU32 to_ImU32(const ColorRGBA& color); + static ImVec4 to_ImVec4(const ColorRGBA& color); + static ColorRGBA from_ImU32(const ImU32& color); + static ColorRGBA from_ImVec4(const ImVec4& color); + + ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id); + static const ImVec4 COL_GREY_DARK; static const ImVec4 COL_GREY_LIGHT; static const ImVec4 COL_ORANGE_DARK; @@ -200,6 +229,7 @@ public: static const ImVec4 COL_WINDOW_BG_DARK; static const ImVec4 COL_SEPARATOR; static const ImVec4 COL_SEPARATOR_DARK; + static const ImVec4 COL_ORCA; //BBS static void on_change_color_mode(bool is_dark); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index cd9a617f4b..18a86fb8ff 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "MeshUtils.hpp" #include "libslic3r/Tesselate.hpp" @@ -5,23 +9,39 @@ #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CSGMesh/SliceCSGMesh.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + #include #include -#include "CameraUtils.hpp" + +#include namespace Slic3r { namespace GUI { +void MeshClipper::set_behaviour(bool fill_cut, double contour_width) +{ + if (fill_cut != m_fill_cut || ! is_approx(contour_width, m_contour_width)) + m_result.reset(); + m_fill_cut = fill_cut; + m_contour_width = contour_width; +} + + + void MeshClipper::set_plane(const ClippingPlane& plane) { if (m_plane != plane) { m_plane = plane; - m_triangles_valid = false; + m_result.reset(); } } @@ -30,27 +50,41 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane) { if (m_limiting_plane != plane) { m_limiting_plane = plane; - m_triangles_valid = false; + m_result.reset(); } } -void MeshClipper::set_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(const indexed_triangle_set& mesh) { - if (m_mesh != &mesh) { + if (m_mesh.get() != &mesh) { m_mesh = &mesh; - m_triangles_valid = false; - m_triangles2d.resize(0); + m_result.reset(); } } -void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(AnyPtr &&ptr) { - if (m_negative_mesh != &mesh) { + if (m_mesh.get() != ptr.get()) { + m_mesh = std::move(ptr); + m_result.reset(); + } +} + +void MeshClipper::set_negative_mesh(const indexed_triangle_set& mesh) +{ + if (m_negative_mesh.get() != &mesh) { m_negative_mesh = &mesh; - m_triangles_valid = false; - m_triangles2d.resize(0); + m_result.reset(); + } +} + +void MeshClipper::set_negative_mesh(AnyPtr &&ptr) +{ + if (m_negative_mesh.get() != ptr.get()) { + m_negative_mesh = std::move(ptr); + m_result.reset(); } } @@ -60,61 +94,157 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) { if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) { m_trafo = trafo; - m_triangles_valid = false; - m_triangles2d.resize(0); + m_result.reset(); } } - - -void MeshClipper::render_cut() +void MeshClipper::render_cut(const ColorRGBA& color, const std::vector* ignore_idxs) { - if (! m_triangles_valid) + if (! m_result) recalculate_triangles(); + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); - if (m_vertex_array.has_VBOs()) - m_vertex_array.render(); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + for (size_t i=0; icut_islands.size(); ++i) { + if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) + continue; + CutIsland& isl = m_result->cut_islands[i]; + isl.model.set_color(isl.disabled ? ColorRGBA(0.5f, 0.5f, 0.5f, 1.f) : color); + isl.model.render(); + } + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); } -bool MeshClipper::is_projection_inside_cut(const Vec3d &point_in) const + +void MeshClipper::render_contour(const ColorRGBA& color, const std::vector* ignore_idxs) +{ + if (! m_result) + recalculate_triangles(); + + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + for (size_t i=0; icut_islands.size(); ++i) { + if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) + continue; + CutIsland& isl = m_result->cut_islands[i]; + isl.model_expanded.set_color(isl.disabled ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : color); + isl.model_expanded.render(); + } + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +} + +int MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const { if (!m_result || m_result->cut_islands.empty()) - return false; + return -1; Vec3d point = m_result->trafo.inverse() * point_in; Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); - for (const CutIsland &isl : m_result->cut_islands) { + for (int i=0; icut_islands.size()); ++i) { + const CutIsland& isl = m_result->cut_islands[i]; if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) - return true; + return i; // TODO: handle intersecting contours } - return false; + return -1; } bool MeshClipper::has_valid_contour() const { - return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland &isl) { return !isl.expoly.empty(); }); + return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } +std::vector MeshClipper::point_per_contour() const +{ + assert(m_result); + std::vector out; + + for (const CutIsland& isl : m_result->cut_islands) { + assert(isl.expoly.contour.size() > 2); + // Now return a point lying inside the contour but not in a hole. + // We do this by taking a point lying close to the edge, repeating + // this several times for different edges and distances from them. + // (We prefer point not extremely close to the border. + bool done = false; + Vec2d p; + size_t i = 1; + while (i < isl.expoly.contour.size()) { + const Vec2d& a = unscale(isl.expoly.contour.points[i-1]); + const Vec2d& b = unscale(isl.expoly.contour.points[i]); + Vec2d n = (b-a).normalized(); + std::swap(n.x(), n.y()); + n.x() = -1 * n.x(); + double f = 10.; + while (f > 0.05) { + p = (0.5*(b+a)) + f * n; + if (isl.expoly.contains(Point::new_scale(p))) { + done = true; + break; + } + f = f/10.; + } + if (done) + break; + i += std::max(size_t(2), isl.expoly.contour.size() / 5); + } + // If the above failed, just return the centroid, regardless of whether + // it is inside the contour or in a hole (we must return something). + Vec2d c = done ? p : unscale(isl.expoly.contour.centroid()); + out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.)); + } + return out; +} + + void MeshClipper::recalculate_triangles() { - const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast(); - // Calculate clipping plane normal in mesh coordinates. - const Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast(); - const Vec3d up = up_noscale.cast().cwiseProduct(m_trafo.get_scaling_factor()); - // Calculate distance from mesh origin to the clipping plane (in mesh coordinates). - const float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm()); + m_result = ClipResult(); + + auto plane_mesh = Eigen::Hyperplane(m_plane.get_normal(), -m_plane.distance(Vec3d::Zero())).transform(m_trafo.get_matrix().inverse()); + const Vec3d up = plane_mesh.normal(); + const float height_mesh = -plane_mesh.offset(); // Now do the cutting MeshSlicingParams slicing_params; slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up, Vec3d::UnitZ())); - ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); + ExPolygons expolys; - if (m_negative_mesh && !m_negative_mesh->empty()) { - const ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params)); - expolys = diff_ex(expolys, neg_expolys); + if (m_csgmesh.empty()) { + if (m_mesh) + expolys = union_ex(slice_mesh(*m_mesh, height_mesh, slicing_params)); + + if (m_negative_mesh && !m_negative_mesh->empty()) { + const ExPolygons neg_expolys = union_ex(slice_mesh(*m_negative_mesh, height_mesh, slicing_params)); + expolys = diff_ex(expolys, neg_expolys); + } + } else { + expolys = std::move(csg::slice_csgmesh_ex(range(m_csgmesh), {height_mesh}, MeshSlicingParamsEx{slicing_params}).front()); } + // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), up); @@ -122,7 +252,6 @@ void MeshClipper::recalculate_triangles() tr.rotate(q); tr = m_trafo.get_matrix() * tr; - m_result = ClipResult(); m_result->trafo = tr; if (m_limiting_plane != ClippingPlane::ClipsNothing()) @@ -170,7 +299,7 @@ void MeshClipper::recalculate_triangles() // it so it lies on our line. This will be the figure to subtract // from the cut. The coordinates must not overflow after the transform, // make the rectangle a bit smaller. - const coord_t size = (std::numeric_limits::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; + const coord_t size = (std::numeric_limits::max()/2 - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4; Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; ep.front().rotate(angle); ep.front().translate(scale_(-e * a), scale_(-e * b)); @@ -178,28 +307,107 @@ void MeshClipper::recalculate_triangles() } } - for (const ExPolygon &exp : expolys) { - m_result->cut_islands.push_back(CutIsland()); - CutIsland &isl = m_result->cut_islands.back(); - isl.expoly = std::move(exp); - isl.expoly_bb = get_extents(exp); - } - - m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); - tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + Transform3d tr2 = tr; + tr2.pretranslate(0.002 * m_plane.get_normal().normalized()); - m_vertex_array.release_geometry(); - for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { - m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); - m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); - m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); - const size_t idx = it - m_triangles2d.cbegin(); - m_vertex_array.push_triangle(idx, idx+1, idx+2); + + std::vector triangles2d; + + for (const ExPolygon& exp : expolys) { + triangles2d.clear(); + + m_result->cut_islands.push_back(CutIsland()); + CutIsland& isl = m_result->cut_islands.back(); + + if (m_fill_cut) { + triangles2d = triangulate_expolygon_2f(exp, m_trafo.get_matrix().matrix().determinant() < 0.); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + } + + if (!init_data.is_empty()) + isl.model.init_from(std::move(init_data)); + } + + if (m_contour_width != 0. && ! exp.contour.empty()) { + triangles2d.clear(); + + // The contours must not scale with the object. Check the scale factor + // in the respective directions, create a scaled copy of the ExPolygon + // offset it and then unscale the result again. + + Transform3d t = tr; + t.translation() = Vec3d::Zero(); + double scale_x = (t * Vec3d::UnitX()).norm(); + double scale_y = (t * Vec3d::UnitY()).norm(); + + // To prevent overflow after scaling, downscale the input if needed: + double extra_scale = 1.; + int32_t limit = int32_t(std::min(std::numeric_limits::max() / (2. * std::max(1., scale_x)), std::numeric_limits::max() / (2. * std::max(1., scale_y)))); + int32_t max_coord = 0; + for (const Point& pt : exp.contour) + max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y()))); + if (max_coord + m_contour_width >= limit) + extra_scale = 0.9 * double(limit) / max_coord; + + ExPolygon exp_copy = exp; + if (extra_scale != 1.) + exp_copy.scale(extra_scale); + exp_copy.scale(scale_x, scale_y); + + ExPolygons expolys_exp = offset_ex(exp_copy, scale_(m_contour_width)); + expolys_exp = diff_ex(expolys_exp, ExPolygons({exp_copy})); + + for (ExPolygon& e : expolys_exp) { + e.scale(1./scale_x, 1./scale_y); + if (extra_scale != 1.) + e.scale(1./extra_scale); + } + + + triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); + GLModel::Geometry init_data = GLModel::Geometry(); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + } + + if (!init_data.is_empty()) + isl.model_expanded.init_from(std::move(init_data)); + } + + isl.expoly = std::move(exp); + isl.expoly_bb = get_extents(isl.expoly); + + Point centroid_scaled = isl.expoly.contour.centroid(); + Vec3d centroid_world = m_result->trafo * Vec3d(unscale(centroid_scaled).x(), unscale(centroid_scaled).y(), 0.); + isl.hash = isl.expoly.contour.size() + size_t(std::abs(100.*centroid_world.x())) + size_t(std::abs(100.*centroid_world.y())) + size_t(std::abs(100.*centroid_world.z())); } - m_vertex_array.finalize_geometry(true); - m_triangles_valid = true; + // Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes + // flips the normal of the cut, in which case the contours stay the same but their order may change. + std::sort(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& a, const CutIsland& b) { + return a.hash < b.hash; + }); } @@ -208,46 +416,26 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const return m_normals[facet_idx]; } -void MeshRaycaster::line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3d &point, Vec3d &direction) +void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) { CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); Transform3d inv = trafo.inverse(); - point = inv * point; - direction = inv.linear() * direction; + point = inv*point; + direction = inv.linear()*direction; } -void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const -{ - Matrix4d modelview = camera.get_view_matrix().matrix(); - Matrix4d projection= camera.get_projection_matrix().matrix(); - Vec4i viewport(camera.get_viewport().data()); - - Vec3d pt1; - Vec3d pt2; - igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 0.), - modelview, projection, viewport, pt1); - igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 1.), - modelview, projection, viewport, pt2); - - Transform3d inv = trafo.inverse(); - pt1 = inv * pt1; - pt2 = inv * pt2; - - point = pt1; - direction = pt2-pt1; -} - - bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, size_t* facet_idx, bool sinking_limit) const { Vec3d point; Vec3d direction; - line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = trafo.inverse(); + point = inv*point; + direction = inv.linear()*direction; - std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector hits = m_emesh.query_ray_hits(point, direction); if (hits.empty()) return false; // no intersection found @@ -280,6 +468,21 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& } + +bool MeshRaycaster::intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const +{ + Transform3d trafo_inv = trafo.inverse(); + Vec3d to = trafo_inv * (point + direction); + point = trafo_inv * point; + direction = (to-point).normalized(); + + std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); + + return !hits.empty() || !neg_hits.empty(); +} + + std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector& points, const ClippingPlane* clipping_plane) const { @@ -298,7 +501,7 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; + std::vector hits; // Offset the start of the ray by EPSILON to account for numerical inaccuracies. hits = m_emesh.query_ray_hits((inverse_trafo * pt.cast() + direction_to_camera_mesh * EPSILON), direction_to_camera_mesh); @@ -328,13 +531,47 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo return out; } +bool MeshRaycaster::closest_hit(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, size_t* facet_idx) const +{ + Vec3d point; + Vec3d direction; + line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); + + const std::vector hits = m_emesh.query_ray_hits(point, direction.normalized()); + + if (hits.empty()) + return false; // no intersection found + + size_t hit_id = 0; + if (clipping_plane != nullptr) { + while (hit_id < hits.size() && clipping_plane->is_point_clipped(trafo * hits[hit_id].position())) { + ++hit_id; + } + } + + if (hit_id == hits.size()) + return false; // all points are obscured or cut by the clipping plane. + + const AABBMesh::hit_result& hit = hits[hit_id]; + + position = hit.position().cast(); + normal = hit.normal().cast(); + + if (facet_idx != nullptr) + *facet_idx = hit.face(); + + return true; +} Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const { int idx = 0; Vec3d closest_point; - m_emesh.squared_distance(point.cast(), idx, closest_point); + Vec3d pointd = point.cast(); + m_emesh.squared_distance(pointd, idx, closest_point); if (normal) + // TODO: consider: get_normal(m_emesh, pointd).cast(); *normal = m_normals[idx]; return closest_point.cast(); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 123b8a7852..c3e0b4247d 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -1,17 +1,25 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_MeshUtils_hpp_ #define slic3r_MeshUtils_hpp_ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" -#include "libslic3r/SLA/IndexedMesh.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/AABBMesh.hpp" +#include "libslic3r/CSGMesh/TriangleMeshAdapter.hpp" +#include "libslic3r/CSGMesh/CSGMeshCopy.hpp" #include "admesh/stl.h" -#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLModel.hpp" #include +#include +#include namespace Slic3r { - namespace GUI { struct Camera; @@ -20,16 +28,14 @@ struct Camera; // lm_FIXME: Following class might possibly be replaced by Eigen::Hyperplane class ClippingPlane { - double m_data[4]; + std::array m_data; public: - ClippingPlane() - { + ClippingPlane() { *this = ClipsNothing(); } - ClippingPlane(const Vec3d& direction, double offset) - { + ClippingPlane(const Vec3d& direction, double offset) { set_normal(direction); set_offset(offset); } @@ -45,8 +51,7 @@ public: } bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; } - void set_normal(const Vec3d& normal) - { + void set_normal(const Vec3d& normal) { const Vec3d norm_dir = normal.normalized(); m_data[0] = norm_dir.x(); m_data[1] = norm_dir.y(); @@ -55,22 +60,28 @@ public: void set_offset(double offset) { m_data[3] = offset; } double get_offset() const { return m_data[3]; } Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); } + void invert_normal() { m_data[0] *= -1.0; m_data[1] *= -1.0; m_data[2] *= -1.0; } + ClippingPlane inverted_normal() const { return ClippingPlane(-get_normal(), get_offset()); } bool is_active() const { return m_data[3] != DBL_MAX; } static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); } - const double* get_data() const { return m_data; } + const std::array& get_data() const { return m_data; } // Serialization through cereal library template - void serialize( Archive & ar ) - { + void serialize( Archive & ar ) { ar( m_data[0], m_data[1], m_data[2], m_data[3] ); } }; // MeshClipper class cuts a mesh and is able to return a triangulated cut. -class MeshClipper { +class MeshClipper +{ public: + // Set whether the cut should be triangulated and whether a cut + // contour should be calculated and shown. + void set_behaviour(bool fill_cut, double contour_width); + // Inform MeshClipper about which plane we want to use to cut the mesh // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); @@ -82,9 +93,25 @@ public: // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. - void set_mesh(const TriangleMesh& mesh); + void set_mesh(const indexed_triangle_set& mesh); + void set_mesh(AnyPtr &&ptr); - void set_negative_mesh(const TriangleMesh &mesh); + void set_negative_mesh(const indexed_triangle_set &mesh); + void set_negative_mesh(AnyPtr &&ptr); + + template + void set_mesh(const Range &csgrange, bool copy_meshes = false) + { + if (! csg::is_same(range(m_csgmesh), csgrange)) { + m_csgmesh.clear(); + if (copy_meshes) + csg::copy_csgrange_deep(csgrange, std::back_inserter(m_csgmesh)); + else + csg::copy_csgrange_shallow(csgrange, std::back_inserter(m_csgmesh)); + + m_result.reset(); + } + } // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. @@ -92,34 +119,41 @@ public: // Render the triangulated cut. Transformation matrices should // be set in world coords. - void render_cut(); + void render_cut(const ColorRGBA& color, const std::vector* ignore_idxs = nullptr); + void render_contour(const ColorRGBA& color, const std::vector* ignore_idxs = nullptr); - bool is_projection_inside_cut(const Vec3d &point) const; + // Returns index of the contour which was clicked, -1 otherwise. + int is_projection_inside_cut(const Vec3d& point) const; bool has_valid_contour() const; + int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; } + std::vector point_per_contour() const; private: void recalculate_triangles(); Geometry::Transformation m_trafo; - const TriangleMesh* m_mesh = nullptr; - const TriangleMesh* m_negative_mesh = nullptr; + AnyPtr m_mesh; + AnyPtr m_negative_mesh; + std::vector m_csgmesh; + ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); - std::vector m_triangles2d; - GLIndexedVertexArray m_vertex_array; - bool m_triangles_valid = false; - struct CutIsland - { - ExPolygon expoly; + struct CutIsland { + GLModel model; + GLModel model_expanded; + ExPolygon expoly; BoundingBox expoly_bb; + bool disabled = false; + size_t hash; }; - struct ClipResult - { + struct ClipResult { std::vector cut_islands; - Transform3d trafo; // this rotates the cut into world coords + Transform3d trafo; // this rotates the cut into world coords }; - std::optional m_result; // the cut plane + std::optional m_result; + bool m_fill_cut = true; + double m_contour_width = 0.; }; @@ -128,19 +162,21 @@ private: // whether certain points are visible or obscured by the mesh etc. class MeshRaycaster { public: - // The class references extern TriangleMesh, which must stay alive - // during MeshRaycaster existence. - MeshRaycaster(const TriangleMesh& mesh) - : m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length - , m_normals(its_face_normals(mesh.its)) + explicit MeshRaycaster(std::shared_ptr mesh) + : m_mesh(std::move(mesh)) + , m_emesh(*m_mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length + , m_normals(its_face_normals(m_mesh->its)) { + assert(m_mesh); } - static void line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, - const Camera &camera, Vec3d &point, Vec3d &direction); + explicit MeshRaycaster(const TriangleMesh &mesh) + : MeshRaycaster(std::make_unique(mesh)) + {} - void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const; + // DEPRICATED - use CameraUtils::ray_from_screen_pos + static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction); // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( @@ -153,6 +189,12 @@ public: size_t* facet_idx = nullptr, // index of the facet hit bool sinking_limit = true ) const; + + const AABBMesh &get_aabb_mesh() const { return m_emesh; } + + // Given a point and direction in world coords, returns whether the respective line + // intersects the mesh if it is transformed into world by trafo. + bool intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const; // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane @@ -164,10 +206,22 @@ public: const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) ) const; + // Returns true if the ray, built from mouse position and camera direction, intersects the mesh. + // In this case, position and normal contain the position and normal, in model coordinates, of the intersection closest to the camera, + // depending on the position/orientation of the clipping_plane, if specified + bool closest_hit( + const Vec2d& mouse_pos, + const Transform3d& trafo, // how to get the mesh into world coords + const Camera& camera, // current camera position + Vec3f& position, // where to save the positibon of the hit (mesh coords) + Vec3f& normal, // normal of the triangle that was hit + const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) + size_t* facet_idx = nullptr // index of the facet hit + ) const; + // Given a point in world coords, the method returns closest point on the mesh. // The output is in mesh coords. // normal* can be used to also get normal of the respective triangle. - Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; // Given a point in mesh coords, the method returns the closest facet from mesh. @@ -176,11 +230,22 @@ public: Vec3f get_triangle_normal(size_t facet_idx) const; private: - sla::IndexedMesh m_emesh; + std::shared_ptr m_mesh; + AABBMesh m_emesh; std::vector m_normals; }; - +struct PickingModel +{ + GLModel model; + std::unique_ptr mesh_raycaster; + + void reset() { + model.reset(); + mesh_raycaster.reset(); + } +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index cceb1b5d88..64060b6958 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "MsgDialog.hpp" #include @@ -14,6 +18,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "I18N.hpp" //#include "ConfigWizard.hpp" @@ -264,9 +269,9 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont monospace = wxGetApp().code_font(); wxColour text_clr = wxGetApp().get_label_clr_default(); - wxColour bgr_clr = parent->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + wxColour bgr_clr = parent->GetBackgroundColour(); + auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue())); + auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue())); const int font_size = font.GetPointSize(); int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size }; html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size); @@ -410,6 +415,58 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& finalize(); } +wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/) +{ + wxString out; + + for (size_t i = 0; i < in.size();) { + // Overwrite the character (space or newline) starting at ibreak? + bool overwrite = false; + // UTF8 representation of wxString. + // Where to break the line, index of character at the start of a UTF-8 sequence. + size_t ibreak = size_t(-1); + // Overwrite the character at ibreak (it is a whitespace) or not? + size_t j = i; + for (size_t cnt = 0; j < in.size();) { + if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) { + // Overwrite the whitespace. + ibreak = j ++; + overwrite = true; + if (newline) + break; + } else if (in[j] == '/' +#ifdef _WIN32 + || in[j] == '\\' +#endif // _WIN32 + ) { + // Insert after the slash. + ibreak = ++ j; + overwrite = false; + } else + j += get_utf8_sequence_length(in.c_str() + j, in.size() - j); + if (++ cnt == line_len) { + if (ibreak == size_t(-1)) { + ibreak = j; + overwrite = false; + } + break; + } + } + if (j == in.size()) { + out.append(in.begin() + i, in.end()); + break; + } + assert(ibreak != size_t(-1)); + out.append(in.begin() + i, in.begin() + ibreak); + out.append('\n'); + i = ibreak; + if (overwrite) + ++ i; + } + + return out; +} + // InfoDialog DownloadDialog::DownloadDialog(wxWindow *parent, const wxString &msg, const wxString &title, bool is_marked_msg /* = false*/, long style /* = wxOK | wxICON_INFORMATION*/) : MsgDialog(parent, title, msg, style), msg(msg) diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index c9b0ded3c5..a136d2eed1 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2022 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_MsgDialog_hpp_ #define slic3r_MsgDialog_hpp_ @@ -128,6 +132,8 @@ public: virtual ~WarningDialog() = default; }; +wxString get_wraped_wxString(const wxString& text_in, size_t line_len = 80); + #if 1 // Generic static line, used intead of wxStaticLine //class StaticLine: public wxTextCtrl diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 3f38e15bc3..12912d750d 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "OpenGLManager.hpp" @@ -65,6 +69,11 @@ const std::string& OpenGLManager::GLInfo::get_renderer() const return m_renderer; } +bool OpenGLManager::GLInfo::is_mesa() const +{ + return boost::icontains(m_version, "mesa"); +} + int OpenGLManager::GLInfo::get_max_tex_size() const { if (!m_detected) @@ -207,7 +216,7 @@ std::string OpenGLManager::GLInfo::to_string(bool for_github) const OpenGLManager::GLInfo OpenGLManager::s_gl_info; bool OpenGLManager::s_compressed_textures_supported = false; -bool OpenGLManager::m_use_manually_generated_mipmaps = true; +bool OpenGLManager::s_force_power_of_two_textures = false; OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown; OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown; @@ -290,31 +299,22 @@ bool OpenGLManager::init_gl(bool popup_error) #ifdef _WIN32 // Since AMD driver version 22.7.1, there is probably some bug in the driver that causes the issue with the missing - // texture of the bed. It seems that this issue only triggers when mipmaps are generated manually - // (combined with a texture compression) and when mipmaps are generated through OpenGL glGenerateMipmap is working. - // So, for newer drivers than 22.6.1, the last working driver version, we use mipmaps generated through OpenGL. - if (const auto gl_info = OpenGLManager::get_gl_info(); boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.")) { - // WHQL drivers seem to have one more version number at the end besides non-WHQL drivers. - // WHQL: 4.6.14800 Compatibility Profile Context 22.6.1 30.0.21023.1015 - // Non-WHQL: 4.6.0 Compatibility Profile Context 22.8.1.220810 - std::regex version_rgx(R"(Compatibility\sProfile\sContext\s(\d+)\.(\d+)\.(\d+))"); - if (std::smatch matches; std::regex_search(gl_info.get_version(), matches, version_rgx) && matches.size() == 4) { - int version_major = std::stoi(matches[1].str()); - int version_minor = std::stoi(matches[2].str()); - int version_patch = std::stoi(matches[3].str()); - BOOST_LOG_TRIVIAL(debug) << "Found AMD driver version: " << version_major << "." << version_minor << "." << version_patch; - - if (version_major > 22 || (version_major == 22 && version_minor > 6) || (version_major == 22 && version_minor == 6 && version_patch > 1)) { - m_use_manually_generated_mipmaps = false; - BOOST_LOG_TRIVIAL(debug) << "Mipmapping through OpenGL was enabled."; - } - } else { - BOOST_LOG_TRIVIAL(error) << "Not recognized format of version."; - } - } else { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "not AMD driver."; - } -#endif + // texture of the bed (see: https://github.com/prusa3d/PrusaSlicer/issues/8417). + // It seems that this issue only triggers when mipmaps are generated manually + // (combined with a texture compression) with texture size not being power of two. + // When mipmaps are generated through OpenGL function glGenerateMipmap() the driver works fine, + // but the mipmap generation is quite slow on some machines. + // There is no an easy way to detect the driver version without using Win32 API because the strings returned by OpenGL + // have no standardized format, only some of them contain the driver version. + // Until we do not know that driver will be fixed (if ever) we force the use of power of two textures on all cards + // 1) containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER) + // 2) containing the string 'Custom' in the string returned by glGetString(GL_RENDERER) + const auto& gl_info = OpenGLManager::get_gl_info(); + if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") && + (boost::contains(gl_info.get_renderer(), "Radeon") || + boost::contains(gl_info.get_renderer(), "Custom"))) + s_force_power_of_two_textures = true; +#endif // _WIN32 } return true; diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index f2670f8af0..65a3dce6e8 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_OpenGLManager_hpp_ #define slic3r_OpenGLManager_hpp_ @@ -40,6 +44,8 @@ public: const std::string& get_vendor() const; const std::string& get_renderer() const; + bool is_mesa() const; + int get_max_tex_size() const; float get_max_anisotropy() const; @@ -81,10 +87,11 @@ private: static OSInfo s_os_info; #endif //__APPLE__ static bool s_compressed_textures_supported; + static bool s_force_power_of_two_textures; + static EMultisampleState s_multisample; static EFramebufferType s_framebuffers_type; - static bool m_use_manually_generated_mipmaps; public: OpenGLManager() = default; ~OpenGLManager(); @@ -101,7 +108,7 @@ public: static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; } static wxGLCanvas* create_wxglcanvas(wxWindow& parent); static const GLInfo& get_gl_info() { return s_gl_info; } - static bool use_manually_generated_mipmaps() { return m_use_manually_generated_mipmaps; } + static bool force_power_of_two_textures() { return s_force_power_of_two_textures; } private: static void detect_multisample(int* attribList); diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index 606baf8c24..725c5fde06 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -591,6 +591,15 @@ void ParamsPanel::set_active_tab(wxPanel* tab) wxString title = cur_tab->type() == Preset::TYPE_FILAMENT ? _L("Filament settings") : _L("Printer settings"); dialog->SetTitle(title); } + + auto tab_print = dynamic_cast(m_tab_print); + if (cur_tab == m_tab_print) { + if (tab_print) + tab_print->toggle_line("print_flow_ratio", false); + } else { + if (tab_print) + tab_print->toggle_line("print_flow_ratio", false); + } } bool ParamsPanel::is_active_and_shown_tab(wxPanel* tab) diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index d07895b3de..edbb541a74 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -64,17 +64,17 @@ namespace GUI { class Bed3D; -std::array PartPlate::SELECT_COLOR = { 0.2666f, 0.2784f, 0.2784f, 1.0f }; //{ 0.4196f, 0.4235f, 0.4235f, 1.0f }; -std::array PartPlate::UNSELECT_COLOR = { 0.82f, 0.82f, 0.82f, 1.0f }; -std::array PartPlate::UNSELECT_DARK_COLOR = { 0.384f, 0.384f, 0.412f, 1.0f }; -std::array PartPlate::DEFAULT_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; -std::array PartPlate::LINE_TOP_COLOR = { 0.89f, 0.89f, 0.89f, 1.0f }; -std::array PartPlate::LINE_TOP_DARK_COLOR = { 0.431f, 0.431f, 0.463f, 1.0f }; -std::array PartPlate::LINE_TOP_SEL_COLOR = { 0.5294f, 0.5451, 0.5333f, 1.0f}; -std::array PartPlate::LINE_TOP_SEL_DARK_COLOR = { 0.298f, 0.298f, 0.3333f, 1.0f}; -std::array PartPlate::LINE_BOTTOM_COLOR = { 0.8f, 0.8f, 0.8f, 0.4f }; -std::array PartPlate::HEIGHT_LIMIT_TOP_COLOR = { 0.6f, 0.6f, 1.0f, 1.0f }; -std::array PartPlate::HEIGHT_LIMIT_BOTTOM_COLOR = { 0.4f, 0.4f, 1.0f, 1.0f }; +ColorRGBA PartPlate::SELECT_COLOR = { 0.2666f, 0.2784f, 0.2784f, 1.0f }; //{ 0.4196f, 0.4235f, 0.4235f, 1.0f }; +ColorRGBA PartPlate::UNSELECT_COLOR = { 0.82f, 0.82f, 0.82f, 1.0f }; +ColorRGBA PartPlate::UNSELECT_DARK_COLOR = { 0.384f, 0.384f, 0.412f, 1.0f }; +ColorRGBA PartPlate::DEFAULT_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; +ColorRGBA PartPlate::LINE_TOP_COLOR = { 0.89f, 0.89f, 0.89f, 1.0f }; +ColorRGBA PartPlate::LINE_TOP_DARK_COLOR = { 0.431f, 0.431f, 0.463f, 1.0f }; +ColorRGBA PartPlate::LINE_TOP_SEL_COLOR = { 0.5294f, 0.5451, 0.5333f, 1.0f}; +ColorRGBA PartPlate::LINE_TOP_SEL_DARK_COLOR = { 0.298f, 0.298f, 0.3333f, 1.0f}; +ColorRGBA PartPlate::LINE_BOTTOM_COLOR = { 0.8f, 0.8f, 0.8f, 0.4f }; +ColorRGBA PartPlate::HEIGHT_LIMIT_TOP_COLOR = { 0.6f, 0.6f, 1.0f, 1.0f }; +ColorRGBA PartPlate::HEIGHT_LIMIT_BOTTOM_COLOR = { 0.4f, 0.4f, 1.0f, 1.0f }; // get text extent with wxMemoryDC void get_text_extent(const wxString &msg, wxCoord &w, wxCoord &h, wxFont *font) @@ -116,20 +116,20 @@ wxFont* find_font(const std::string& text_str, int max_size = 32) } void PartPlate::update_render_colors() { - PartPlate::SELECT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Selected]); - PartPlate::UNSELECT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Unselected]); - PartPlate::DEFAULT_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Default]); - PartPlate::LINE_TOP_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Line_Top]); - PartPlate::LINE_BOTTOM_COLOR = GLColor(RenderColor::colors[RenderCol_Plate_Line_Bottom]); + PartPlate::SELECT_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Plate_Selected]); + PartPlate::UNSELECT_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Plate_Unselected]); + PartPlate::DEFAULT_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Plate_Default]); + PartPlate::LINE_TOP_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Plate_Line_Top]); + PartPlate::LINE_BOTTOM_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Plate_Line_Bottom]); } void PartPlate::load_render_colors() { - RenderColor::colors[RenderCol_Plate_Selected] = IMColor(SELECT_COLOR); - RenderColor::colors[RenderCol_Plate_Unselected] = IMColor(UNSELECT_COLOR); - RenderColor::colors[RenderCol_Plate_Default] = IMColor(DEFAULT_COLOR); - RenderColor::colors[RenderCol_Plate_Line_Top] = IMColor(LINE_TOP_COLOR); - RenderColor::colors[RenderCol_Plate_Line_Bottom] = IMColor(LINE_BOTTOM_COLOR); + RenderColor::colors[RenderCol_Plate_Selected] = ImGuiWrapper::to_ImVec4(SELECT_COLOR); + RenderColor::colors[RenderCol_Plate_Unselected] = ImGuiWrapper::to_ImVec4(UNSELECT_COLOR); + RenderColor::colors[RenderCol_Plate_Default] = ImGuiWrapper::to_ImVec4(DEFAULT_COLOR); + RenderColor::colors[RenderCol_Plate_Line_Top] = ImGuiWrapper::to_ImVec4(LINE_TOP_COLOR); + RenderColor::colors[RenderCol_Plate_Line_Bottom] = ImGuiWrapper::to_ImVec4(LINE_BOTTOM_COLOR); } @@ -151,7 +151,6 @@ PartPlate::~PartPlate() clear(); //if (m_quadric != nullptr) // ::gluDeleteQuadric(m_quadric); - release_opengl_resource(); //boost::nowide::remove(m_tmp_gcode_path.c_str()); } @@ -170,7 +169,6 @@ void PartPlate::init() m_print_index = -1; m_print = nullptr; - m_plate_name_vbo_id = 0; } BedType PartPlate::get_bed_type(bool load_from_project) const @@ -333,17 +331,86 @@ void PartPlate::calc_bounding_boxes() const { } } -void PartPlate::calc_triangles(const ExPolygon& poly) { - if (!m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) +void PartPlate::calc_triangles(const ExPolygon &poly) +{ + m_triangles.reset(); + + if (!init_model_from_poly(m_triangles.model, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create plate triangles\n"; } -void PartPlate::calc_exclude_triangles(const ExPolygon& poly) { - if (!m_exclude_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) +void PartPlate::calc_exclude_triangles(const ExPolygon &poly) +{ + m_exclude_triangles.reset(); + + if (!init_model_from_poly(m_exclude_triangles, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create exclude triangles\n"; } +static bool init_model_from_lines(GLModel &model, const Lines &lines, float z) +{ + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * lines.size()); + init_data.reserve_indices(2 * lines.size()); + + for (const auto &l : lines) { + init_data.add_vertex(Vec3f(unscale(l.a.x()), unscale(l.a.y()), z)); + init_data.add_vertex(Vec3f(unscale(l.b.x()), unscale(l.b.y()), z)); + const unsigned int vertices_counter = (unsigned int)init_data.vertices_count(); + init_data.add_line(vertices_counter - 2, vertices_counter - 1); + } + + model.init_from(std::move(init_data)); + + return true; +} + +static bool init_model_from_lines(GLModel &model, const Lines3 &lines) +{ + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * lines.size()); + init_data.reserve_indices(2 * lines.size()); + + for (const auto &l : lines) { + init_data.add_vertex(Vec3f(unscale(l.a.x()), unscale(l.a.y()), unscale(l.a.z()))); + init_data.add_vertex(Vec3f(unscale(l.b.x()), unscale(l.b.y()), unscale(l.b.z()))); + const unsigned int vertices_counter = (unsigned int) init_data.vertices_count(); + init_data.add_line(vertices_counter - 2, vertices_counter - 1); + } + + model.init_from(std::move(init_data)); + + return true; +} + +static void init_raycaster_from_model(PickingModel& model) +{ + assert(model.mesh_raycaster == nullptr); + + const GLModel::Geometry &geometry = model.model.get_geometry(); + + indexed_triangle_set its; + its.vertices.reserve(geometry.vertices_count()); + for (size_t i = 0; i < geometry.vertices_count(); ++i) { + its.vertices.emplace_back(geometry.extract_position_3(i)); + } + its.indices.reserve(geometry.indices_count() / 3); + for (size_t i = 0; i < geometry.indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(geometry.extract_index(tri_id), geometry.extract_index(tri_id + 1), geometry.extract_index(tri_id + 2)); + } + + model.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); +} + void PartPlate::calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox) { + m_gridlines.reset(); + m_gridlines_bolder.reset(); + Polylines axes_lines, axes_lines_bolder; int count = 0; for (coord_t x = pp_bbox.min(0); x <= pp_bbox.max(0); x += scale_(10.0)) { @@ -379,14 +446,18 @@ void PartPlate::calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox Lines contour_lines = to_lines(poly); std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); - if (!m_gridlines.set_from_lines(gridlines, GROUND_Z_GRIDLINE)) + if (!init_model_from_lines(m_gridlines, gridlines, GROUND_Z_GRIDLINE)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create bed grid lines\n"; - if (!m_gridlines_bolder.set_from_lines(gridlines_bolder, GROUND_Z_GRIDLINE)) + if (!init_model_from_lines(m_gridlines_bolder, gridlines_bolder, GROUND_Z_GRIDLINE)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create bed grid lines\n"; } void PartPlate::calc_height_limit() { + m_height_limit_common.reset(); + m_height_limit_bottom.reset(); + m_height_limit_top.reset(); + Lines3 bottom_h_lines, top_lines, top_h_lines, common_lines; int shape_count = m_shape.size(); float first_z = 0.02f; @@ -418,18 +489,20 @@ void PartPlate::calc_height_limit() { //std::copy(bottom_lines.begin(), bottom_lines.end(), std::back_inserter(bottom_h_lines)); std::copy(top_lines.begin(), top_lines.end(), std::back_inserter(top_h_lines)); - if (!m_height_limit_common.set_from_3d_Lines(common_lines)) + if (!init_model_from_lines(m_height_limit_common, common_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit bottom lines\n"; - if (!m_height_limit_bottom.set_from_3d_Lines(bottom_h_lines)) + if (!init_model_from_lines(m_height_limit_bottom, bottom_h_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit bottom lines\n"; - if (!m_height_limit_top.set_from_3d_Lines(top_h_lines)) + if (!init_model_from_lines(m_height_limit_top, top_h_lines)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit top lines\n"; } -void PartPlate::calc_vertex_for_number(int index, bool one_number, GeometryBuffer &buffer) +void PartPlate::calc_vertex_for_number(int index, bool one_number, GLModel &buffer) { + buffer.reset(); + ExPolygon poly; #if 0 //in the up area Vec2d& p = m_shape[2]; @@ -449,13 +522,14 @@ void PartPlate::calc_vertex_for_number(int index, bool one_number, GeometryBuffe poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y) }); #endif - auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); - if (!buffer.set_from_triangles(triangles, GROUND_Z)) + if (!init_model_from_poly(buffer, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } -void PartPlate::calc_vertex_for_icons(int index, GeometryBuffer &buffer) +void PartPlate::calc_vertex_for_icons(int index, PickingModel &model) { + model.reset(); + ExPolygon poly; auto bed_ext = get_extents(m_shape); Vec2d p = bed_ext[2]; @@ -468,13 +542,17 @@ void PartPlate::calc_vertex_for_icons(int index, GeometryBuffer &buffer) poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP) }); - auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); - if (!buffer.set_from_triangles(triangles, GROUND_Z)) + if (!init_model_from_poly(model.model, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; + + init_raycaster_from_model(model); } -void PartPlate::calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer) +/* +void PartPlate::calc_vertex_for_icons_background(int icon_count, GLModel &buffer) { + buffer.reset(); + ExPolygon poly; auto bed_ext = get_extents(m_shape); Vec2d p = bed_ext[2]; @@ -484,38 +562,37 @@ void PartPlate::calc_vertex_for_icons_background(int icon_count, GeometryBuffer poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - PARTPLATE_ICON_GAP_TOP)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - PARTPLATE_ICON_GAP_TOP) }); - auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); - if (!buffer.set_from_triangles(triangles, GROUND_Z)) + if (!init_model_from_poly(buffer, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } +*/ -void PartPlate::render_background(bool force_default_color) const { - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - +void PartPlate::render_background(bool force_default_color) +{ //return directly for current plate if (m_selected && !force_default_color) return; // draw background glsafe(::glDepthMask(GL_FALSE)); + ColorRGBA color; if (!force_default_color) { if (m_selected) { - glsafe(::glColor4fv(PartPlate::SELECT_COLOR.data())); + color = PartPlate::SELECT_COLOR; } else { - glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(PartPlate::UNSELECT_DARK_COLOR.data()) : ::glColor4fv(PartPlate::UNSELECT_COLOR.data())); + color = m_partplate_list->m_is_dark ? PartPlate::UNSELECT_DARK_COLOR : PartPlate::UNSELECT_COLOR; } } else { - glsafe(::glColor4fv(PartPlate::DEFAULT_COLOR.data())); + color = PartPlate::DEFAULT_COLOR; } - glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); - glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + m_triangles.model.set_color(color); + m_triangles.model.render(); glsafe(::glDepthMask(GL_TRUE)); } -void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffer& logo_buffer, bool bottom, unsigned int vbo_id) const +void PartPlate::render_logo_texture(GLTexture &logo_texture, GLModel& logo_buffer, bool bottom) { //check valid if (logo_texture.unsent_compressed_data_available()) { @@ -523,53 +600,37 @@ void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffe logo_texture.send_compressed_data_to_gpu(); } - if (logo_buffer.get_vertices_count() > 0) { + if (logo_buffer.is_initialized()) { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("transparent_background", 0); shader->set_uniform("svg_source", 0); //glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDepthMask(GL_FALSE)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + if (bottom) glsafe(::glFrontFace(GL_CW)); - unsigned int stride = logo_buffer.get_vertex_data_size(); - - GLint position_id = shader->get_attrib_location("v_position"); - GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); - if (position_id != -1) { - glsafe(::glEnableVertexAttribArray(position_id)); - } - if (tex_coords_id != -1) { - glsafe(::glEnableVertexAttribArray(tex_coords_id)); - } - // show the temporary texture while no compressed data is available GLuint tex_id = (GLuint)logo_texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); - - if (position_id != -1) - glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)logo_buffer.get_position_offset())); - if (tex_coords_id != -1) - glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)logo_buffer.get_tex_coords_offset())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)logo_buffer.get_vertices_count())); - - if (tex_coords_id != -1) - glsafe(::glDisableVertexAttribArray(tex_coords_id)); - - if (position_id != -1) - glsafe(::glDisableVertexAttribArray(position_id)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + logo_buffer.render(); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); if (bottom) glsafe(::glFrontFace(GL_CCW)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDepthMask(GL_TRUE)); shader->stop_using(); @@ -577,7 +638,7 @@ void PartPlate::render_logo_texture(GLTexture &logo_texture, const GeometryBuffe } } -void PartPlate::render_logo(bool bottom, bool render_cali) const +void PartPlate::render_logo(bool bottom, bool render_cali) { if (!m_partplate_list->render_bedtype_logo) { // render third-party printer texture logo @@ -643,15 +704,8 @@ void PartPlate::render_logo(bool bottom, bool render_cali) const //canvas.request_extra_frame(); } - if (m_vbo_id == 0) { - unsigned int* vbo_id_ptr = const_cast(&m_vbo_id); - glsafe(::glGenBuffers(1, vbo_id_ptr)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id_ptr)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_logo_triangles.get_vertices_data_size(), (const GLvoid*)m_logo_triangles.get_vertices_data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - if (m_vbo_id != 0 && m_logo_triangles.get_vertices_count() > 0) - render_logo_texture(m_partplate_list->m_logo_texture, m_logo_triangles, bottom, m_vbo_id); + if (m_logo_triangles.is_initialized()) + render_logo_texture(m_partplate_list->m_logo_texture, m_logo_triangles, bottom); return; } @@ -669,7 +723,7 @@ void PartPlate::render_logo(bool bottom, bool render_cali) const // render bed textures for (auto &part : m_partplate_list->bed_texture_info[bed_type_idx].parts) { if (part.texture) { - if (part.buffer && part.buffer->get_vertices_count() > 0 + if (part.buffer && part.buffer->is_initialized() //&& part.vbo_id != 0 ) { if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) { @@ -678,8 +732,7 @@ void PartPlate::render_logo(bool bottom, bool render_cali) const } render_logo_texture(*(part.texture), *(part.buffer), - bottom, - part.vbo_id); + bottom); } } } @@ -688,29 +741,27 @@ void PartPlate::render_logo(bool bottom, bool render_cali) const if (render_cali) { for (auto& part : m_partplate_list->cali_texture_info.parts) { if (part.texture) { - if (part.buffer && part.buffer->get_vertices_count() > 0) { + if (part.buffer && part.buffer->is_initialized()) { if (part.offset.x() != m_origin.x() || part.offset.y() != m_origin.y()) { part.offset = Vec2d(m_origin.x(), m_origin.y()); part.update_buffer(); } render_logo_texture(*(part.texture), *(part.buffer), - bottom, - part.vbo_id); + bottom); } } } } } -void PartPlate::render_exclude_area(bool force_default_color) const { +void PartPlate::render_exclude_area(bool force_default_color) { if (force_default_color) //for thumbnail case return; - unsigned int triangles_vcount = m_exclude_triangles.get_vertices_count(); - std::array select_color{ 0.765f, 0.7686f, 0.7686f, 1.0f }; - std::array unselect_color{ 0.9f, 0.9f, 0.9f, 1.0f }; - std::array default_color{ 0.9f, 0.9f, 0.9f, 1.0f }; + ColorRGBA select_color{ 0.765f, 0.7686f, 0.7686f, 1.0f }; + ColorRGBA unselect_color{ 0.9f, 0.9f, 0.9f, 1.0f }; + //ColorRGBA default_color{ 0.9f, 0.9f, 0.9f, 1.0f }; // draw exclude area glsafe(::glDepthMask(GL_FALSE)); @@ -722,14 +773,13 @@ void PartPlate::render_exclude_area(bool force_default_color) const { glsafe(::glColor4fv(unselect_color.data())); } - glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); - glsafe(::glVertexPointer(3, GL_FLOAT, m_exclude_triangles.get_vertex_data_size(), (GLvoid*)m_exclude_triangles.get_vertices_data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + m_exclude_triangles.set_color(m_selected ? select_color : unselect_color); + m_exclude_triangles.render(); glsafe(::glDepthMask(GL_TRUE)); } -/*void PartPlate::render_background_for_picking(const float* render_color) const +/*void PartPlate::render_background_for_picking(const ColorRGBA render_color) const { unsigned int triangles_vcount = m_triangles.get_vertices_count(); @@ -741,99 +791,68 @@ void PartPlate::render_exclude_area(bool force_default_color) const { glsafe(::glDepthMask(GL_TRUE)); }*/ -void PartPlate::render_grid(bool bottom) const { +void PartPlate::render_grid(bool bottom) { //glsafe(::glEnable(GL_MULTISAMPLE)); // draw grid glsafe(::glLineWidth(1.0f * m_scale_factor)); + + ColorRGBA color; if (bottom) - glsafe(::glColor4fv(LINE_BOTTOM_COLOR.data())); + color = LINE_BOTTOM_COLOR; else { if (m_selected) - glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(LINE_TOP_SEL_DARK_COLOR.data()) : ::glColor4fv(LINE_TOP_SEL_COLOR.data())); + color = m_partplate_list->m_is_dark ? LINE_TOP_SEL_DARK_COLOR : LINE_TOP_SEL_COLOR; else - glsafe(m_partplate_list->m_is_dark ? ::glColor4fv(LINE_TOP_DARK_COLOR.data()) : ::glColor4fv(LINE_TOP_COLOR.data())); + color = m_partplate_list->m_is_dark ? LINE_TOP_DARK_COLOR : LINE_TOP_COLOR; } - glsafe(::glVertexPointer(3, GL_FLOAT, m_gridlines.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); + m_gridlines.set_color(color); + m_gridlines.render(); glsafe(::glLineWidth(2.0f * m_scale_factor)); - glsafe(::glVertexPointer(3, GL_FLOAT, m_gridlines_bolder.get_vertex_data_size(), (GLvoid*)m_gridlines_bolder.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines_bolder.get_vertices_count())); + m_gridlines_bolder.set_color(color); + m_gridlines_bolder.render(); } -void PartPlate::render_height_limit(PartPlate::HeightLimitMode mode) const +void PartPlate::render_height_limit(PartPlate::HeightLimitMode mode) { if (m_print && m_print->config().print_sequence == PrintSequence::ByObject && mode != HEIGHT_LIMIT_NONE) { // draw lower limit glsafe(::glLineWidth(3.0f * m_scale_factor)); - glsafe(::glColor4fv(HEIGHT_LIMIT_BOTTOM_COLOR.data())); - glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_common.get_vertex_data_size(), (GLvoid*)m_height_limit_common.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_common.get_vertices_count())); + m_height_limit_common.set_color(HEIGHT_LIMIT_BOTTOM_COLOR); + m_height_limit_common.render(); if ((mode == HEIGHT_LIMIT_BOTTOM) || (mode == HEIGHT_LIMIT_BOTH)) { glsafe(::glLineWidth(3.0f * m_scale_factor)); - glsafe(::glColor4fv(HEIGHT_LIMIT_BOTTOM_COLOR.data())); - glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_bottom.get_vertex_data_size(), (GLvoid*)m_height_limit_bottom.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_bottom.get_vertices_count())); + m_height_limit_bottom.set_color(HEIGHT_LIMIT_BOTTOM_COLOR); + m_height_limit_bottom.render(); } // draw upper limit if ((mode == HEIGHT_LIMIT_TOP) || (mode == HEIGHT_LIMIT_BOTH)){ - glsafe(::glLineWidth(3.0f * m_scale_factor)); - glsafe(::glColor4fv(HEIGHT_LIMIT_TOP_COLOR.data())); - glsafe(::glVertexPointer(3, GL_FLOAT, m_height_limit_top.get_vertex_data_size(), (GLvoid*)m_height_limit_top.get_vertices_data())); - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_height_limit_top.get_vertices_count())); + glsafe(::glLineWidth(3.0f * m_scale_factor)); + m_height_limit_top.set_color(HEIGHT_LIMIT_TOP_COLOR); + m_height_limit_top.render(); } } } - -void PartPlate::render_icon_texture(int position_id, int tex_coords_id, const GeometryBuffer &buffer, GLTexture &texture, unsigned int &vbo_id) const +void PartPlate::render_icon_texture(GLModel &buffer, GLTexture &texture) { - if (vbo_id == 0) { - glsafe(::glGenBuffers(1, &vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)buffer.get_vertices_data_size(), (const GLvoid*)buffer.get_vertices_data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - - unsigned int stride = buffer.get_vertex_data_size(); GLuint tex_id = (GLuint)texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, vbo_id)); - if (position_id != -1) - glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)buffer.get_position_offset())); - if (tex_coords_id != -1) - glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)buffer.get_tex_coords_offset())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)buffer.get_vertices_count())); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + buffer.render(); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } -void PartPlate::render_plate_name_texture(int position_id, int tex_coords_id) + +void PartPlate::render_plate_name_texture() { if (m_name_texture.get_id() == 0) generate_plate_name_texture(); - if (m_plate_name_vbo_id == 0 && m_plate_name_icon.get_vertices_data_size() > 0) { - glsafe(::glGenBuffers(1, &m_plate_name_vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_plate_name_vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_plate_name_icon.get_vertices_data_size(), (const GLvoid*)m_plate_name_icon.get_vertices_data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - - unsigned int stride = m_plate_name_icon.get_vertex_data_size(); GLuint tex_id = (GLuint)m_name_texture.get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_plate_name_vbo_id)); - if (position_id != -1) - glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_plate_name_icon.get_position_offset())); - if (tex_coords_id != -1) - glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_plate_name_icon.get_tex_coords_offset())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_plate_name_icon.get_vertices_count())); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + m_plate_name_icon.render(); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } @@ -857,6 +876,9 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id) GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("transparent_background", bottom); //shader->set_uniform("svg_source", boost::algorithm::iends_with(m_partplate_list->m_del_texture.get_source(), ".svg")); shader->set_uniform("svg_source", 0); @@ -865,92 +887,72 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id) // glsafe(::glFrontFace(GL_CW)); glsafe(::glDepthMask(GL_FALSE)); - GLint position_id = shader->get_attrib_location("v_position"); - GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); - if (position_id != -1) { - glsafe(::glEnableVertexAttribArray(position_id)); - } - if (tex_coords_id != -1) { - glsafe(::glEnableVertexAttribArray(tex_coords_id)); - } + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + if (!only_name) { if (hover_id == 1) { - render_icon_texture(position_id, tex_coords_id, m_del_icon, m_partplate_list->m_del_hovered_texture, - m_del_vbo_id); + render_icon_texture(m_del_icon.model, m_partplate_list->m_del_hovered_texture); show_tooltip(_u8L("Remove current plate (if not last one)")); } else - render_icon_texture(position_id, tex_coords_id, m_del_icon, m_partplate_list->m_del_texture, - m_del_vbo_id); + render_icon_texture(m_del_icon.model, m_partplate_list->m_del_texture); if (hover_id == 2) { - render_icon_texture(position_id, tex_coords_id, m_orient_icon, - m_partplate_list->m_orient_hovered_texture, m_orient_vbo_id); + render_icon_texture(m_orient_icon.model, m_partplate_list->m_orient_hovered_texture); show_tooltip(_u8L("Auto orient objects on current plate")); } else - render_icon_texture(position_id, tex_coords_id, m_orient_icon, m_partplate_list->m_orient_texture, - m_orient_vbo_id); + render_icon_texture(m_orient_icon.model, m_partplate_list->m_orient_texture); if (hover_id == 3) { - render_icon_texture(position_id, tex_coords_id, m_arrange_icon, - m_partplate_list->m_arrange_hovered_texture, m_arrange_vbo_id); + render_icon_texture(m_arrange_icon.model, m_partplate_list->m_arrange_hovered_texture); show_tooltip(_u8L("Arrange objects on current plate")); } else - render_icon_texture(position_id, tex_coords_id, m_arrange_icon, m_partplate_list->m_arrange_texture, - m_arrange_vbo_id); + render_icon_texture(m_arrange_icon.model, m_partplate_list->m_arrange_texture); if (hover_id == 4) { if (this->is_locked()) { - render_icon_texture(position_id, tex_coords_id, m_lock_icon, - m_partplate_list->m_locked_hovered_texture, m_lock_vbo_id); + render_icon_texture(m_lock_icon.model, + m_partplate_list->m_locked_hovered_texture); show_tooltip(_u8L("Unlock current plate")); } else { - render_icon_texture(position_id, tex_coords_id, m_lock_icon, - m_partplate_list->m_lockopen_hovered_texture, m_lock_vbo_id); + render_icon_texture(m_lock_icon.model, + m_partplate_list->m_lockopen_hovered_texture); show_tooltip(_u8L("Lock current plate")); } } else { if (this->is_locked()) - render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_locked_texture, - m_lock_vbo_id); + render_icon_texture(m_lock_icon.model, m_partplate_list->m_locked_texture); else - render_icon_texture(position_id, tex_coords_id, m_lock_icon, m_partplate_list->m_lockopen_texture, - m_lock_vbo_id); + render_icon_texture(m_lock_icon.model, m_partplate_list->m_lockopen_texture); } if (m_partplate_list->render_plate_settings) { if (hover_id == 5) { if (get_bed_type() == BedType::btDefault && get_print_seq() == PrintSequence::ByDefault && get_first_layer_print_sequence().empty()) - render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_hovered_texture, m_plate_settings_vbo_id); + render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_hovered_texture); else - render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, - m_partplate_list->m_plate_settings_changed_hovered_texture, - m_plate_settings_vbo_id); + render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_changed_hovered_texture); show_tooltip(_u8L("Customize current plate")); } else { if (get_bed_type() == BedType::btDefault && get_print_seq() == PrintSequence::ByDefault && get_first_layer_print_sequence().empty()) - render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, m_partplate_list->m_plate_settings_texture, m_plate_settings_vbo_id); + render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_texture); else - render_icon_texture(position_id, tex_coords_id, m_plate_settings_icon, - m_partplate_list->m_plate_settings_changed_texture, m_plate_settings_vbo_id); + render_icon_texture(m_plate_settings_icon.model, m_partplate_list->m_plate_settings_changed_texture); } } if (m_plate_index >= 0 && m_plate_index < MAX_PLATE_COUNT) { - render_icon_texture(position_id, tex_coords_id, m_plate_idx_icon, - m_partplate_list->m_idx_textures[m_plate_index], m_plate_idx_vbo_id); + render_icon_texture(m_plate_idx_icon, m_partplate_list->m_idx_textures[m_plate_index]); } } - render_plate_name_texture(position_id, tex_coords_id); - if (tex_coords_id != -1) - glsafe(::glDisableVertexAttribArray(tex_coords_id)); + render_plate_name_texture(); - if (position_id != -1) - glsafe(::glDisableVertexAttribArray(position_id)); + glsafe(::glDisable(GL_BLEND)); //if (bottom) // glsafe(::glFrontFace(GL_CCW)); @@ -960,11 +962,14 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id) } } -void PartPlate::render_only_numbers(bool bottom) const +void PartPlate::render_only_numbers(bool bottom) { GLShaderProgram* shader = wxGetApp().get_shader("printbed"); if (shader != nullptr) { shader->start_using(); + const Camera &camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("transparent_background", bottom); //shader->set_uniform("svg_source", boost::algorithm::iends_with(m_partplate_list->m_del_texture.get_source(), ".svg")); shader->set_uniform("svg_source", 0); @@ -973,24 +978,14 @@ void PartPlate::render_only_numbers(bool bottom) const // glsafe(::glFrontFace(GL_CW)); glsafe(::glDepthMask(GL_FALSE)); - GLint position_id = shader->get_attrib_location("v_position"); - GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); - if (position_id != -1) { - glsafe(::glEnableVertexAttribArray(position_id)); - } - if (tex_coords_id != -1) { - glsafe(::glEnableVertexAttribArray(tex_coords_id)); - } + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); if (m_plate_index >=0 && m_plate_index < MAX_PLATE_COUNT) { - render_icon_texture(position_id, tex_coords_id, m_plate_idx_icon, m_partplate_list->m_idx_textures[m_plate_index], m_plate_idx_vbo_id); + render_icon_texture(m_plate_idx_icon, m_partplate_list->m_idx_textures[m_plate_index]); } - if (tex_coords_id != -1) - glsafe(::glDisableVertexAttribArray(tex_coords_id)); - - if (position_id != -1) - glsafe(::glDisableVertexAttribArray(position_id)); + glsafe(::glDisable(GL_BLEND)); //if (bottom) // glsafe(::glFrontFace(GL_CCW)); @@ -1000,20 +995,7 @@ void PartPlate::render_only_numbers(bool bottom) const } } -void PartPlate::render_rectangle_for_picking(const GeometryBuffer &buffer, const float* render_color) const -{ - unsigned int triangles_vcount = buffer.get_vertices_count(); - - //glsafe(::glDepthMask(GL_FALSE)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); - glsafe(::glColor4fv(render_color)); - glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); - glsafe(::glVertexPointer(3, GL_FLOAT, buffer.get_vertex_data_size(), (GLvoid*)buffer.get_vertices_data())); - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - //glsafe(::glDepthMask(GL_TRUE)); -} - +/* void PartPlate::render_label(GLCanvas3D& canvas) const { std::string label = (boost::format("Plate %1%") % (m_plate_index + 1)).str(); const Camera& camera = wxGetApp().plater()->get_camera(); @@ -1058,14 +1040,14 @@ void PartPlate::render_label(GLCanvas3D& canvas) const { } -void PartPlate::render_grabber(const float* render_color, bool use_lighting) const +void PartPlate::render_grabber(const ColorRGBA render_color, bool use_lighting) const { BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); const Vec3d& center = m_grabber_box.center(); if (use_lighting) glsafe(::glEnable(GL_LIGHTING)); - glsafe(::glColor4fv(render_color)); + glsafe(::glColor4fv(render_color.data())); glsafe(::glPushMatrix()); glsafe(::glTranslated(center(0), center(1), center(2))); @@ -1138,7 +1120,7 @@ void PartPlate::render_face(float x_size, float y_size) const glsafe(::glEnd()); } -void PartPlate::render_arrows(const float* render_color, bool use_lighting) const +void PartPlate::render_arrows(const ColorRGBA render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) @@ -1176,7 +1158,7 @@ void PartPlate::render_arrows(const float* render_color, bool use_lighting) cons #endif } -void PartPlate::render_left_arrow(const float* render_color, bool use_lighting) const +void PartPlate::render_left_arrow(const ColorRGBA render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) @@ -1203,7 +1185,7 @@ void PartPlate::render_left_arrow(const float* render_color, bool use_lighting) glsafe(::glDisable(GL_LIGHTING)); #endif } -void PartPlate::render_right_arrow(const float* render_color, bool use_lighting) const +void PartPlate::render_right_arrow(const ColorRGBA render_color, bool use_lighting) const { #if 0 if (m_quadric == nullptr) @@ -1229,103 +1211,28 @@ void PartPlate::render_right_arrow(const float* render_color, bool use_lighting) glsafe(::glDisable(GL_LIGHTING)); #endif } +*/ -void PartPlate::on_render_for_picking() const { - //glsafe(::glDisable(GL_DEPTH_TEST)); - int hover_id = 0; - std::array color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; - //render_grabber(m_grabber_color, false); - render_rectangle_for_picking(m_triangles, m_grabber_color); - hover_id = 1; - color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; - //render_left_arrow(m_grabber_color, false); - render_rectangle_for_picking(m_del_icon, m_grabber_color); - hover_id = 2; - color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; - render_rectangle_for_picking(m_orient_icon, m_grabber_color); - hover_id = 3; - color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; - render_rectangle_for_picking(m_arrange_icon, m_grabber_color); - hover_id = 4; - color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; - //render_right_arrow(m_grabber_color, false); - render_rectangle_for_picking(m_lock_icon, m_grabber_color); - hover_id = 5; - color = picking_color_component(hover_id); - m_grabber_color[0] = color[0]; - m_grabber_color[1] = color[1]; - m_grabber_color[2] = color[2]; - m_grabber_color[3] = color[3]; +static void register_model_for_picking(GLCanvas3D &canvas, PickingModel &model, int id) +{ + canvas.add_raycaster_for_picking(SceneRaycaster::EType::Bed, id, *model.mesh_raycaster, Transform3d::Identity()); +} + +void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas) +{ + register_model_for_picking(canvas, m_triangles, picking_id_component(0)); + register_model_for_picking(canvas, m_del_icon, picking_id_component(1)); + register_model_for_picking(canvas, m_orient_icon, picking_id_component(2)); + register_model_for_picking(canvas, m_arrange_icon, picking_id_component(3)); + register_model_for_picking(canvas, m_lock_icon, picking_id_component(4)); if (m_partplate_list->render_plate_settings) - render_rectangle_for_picking(m_plate_settings_icon, m_grabber_color); + register_model_for_picking(canvas, m_plate_settings_icon, picking_id_component(5)); } -std::array PartPlate::picking_color_component(int idx) const +int PartPlate::picking_id_component(int idx) const { - static const float INV_255 = 1.0f / 255.0f; unsigned int id = PLATE_BASE_ID - this->m_plate_index * GRABBER_COUNT - idx; - return std::array { - float((id >> 0) & 0xff)* INV_255, // red - float((id >> 8) & 0xff)* INV_255, // greeen - float((id >> 16) & 0xff)* INV_255, // blue - float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 - }; -} - -void PartPlate::release_opengl_resource() -{ - if (m_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_vbo_id)); - m_vbo_id = 0; - } - if (m_del_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_del_vbo_id)); - m_del_vbo_id = 0; - } - if (m_orient_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_orient_vbo_id)); - m_orient_vbo_id = 0; - } - if (m_arrange_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_arrange_vbo_id)); - m_arrange_vbo_id = 0; - } - if (m_lock_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_lock_vbo_id)); - m_lock_vbo_id = 0; - } - if (m_plate_settings_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_plate_settings_vbo_id)); - m_plate_settings_vbo_id = 0; - } - if (m_plate_idx_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_plate_idx_vbo_id)); - m_plate_idx_vbo_id = 0; - } - if (m_plate_name_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_plate_name_vbo_id)); - m_plate_name_vbo_id = 0; - } + return id; } std::vector PartPlate::get_extruders(bool conside_custom_gcode) const @@ -1764,6 +1671,8 @@ Vec3d PartPlate::get_center_origin() void PartPlate::generate_plate_name_texture() { + m_plate_name_icon.reset(); + // generate m_name_texture texture from m_name with generate_from_text_string m_name_texture.reset(); auto text = m_name.empty()? _L("Untitled") : from_u8(m_name); @@ -1786,14 +1695,8 @@ void PartPlate::generate_plate_name_texture() poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w - offset_x), scale_(p(1) - PARTPLATE_TEXT_OFFSET_Y)}); poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) - PARTPLATE_TEXT_OFFSET_Y) }); - auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); - if (!m_plate_name_icon.set_from_triangles(triangles, GROUND_Z)) + if (!init_model_from_poly(m_plate_name_icon, poly, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; - - if (m_plate_name_vbo_id > 0) { - glsafe(::glDeleteBuffers(1, &m_plate_name_vbo_id)); - m_plate_name_vbo_id = 0; - } } void PartPlate::set_plate_name(const std::string& name) { @@ -2499,11 +2402,9 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, Ve ExPolygon logo_poly; generate_logo_polygon(logo_poly); - if (!m_logo_triangles.set_from_triangles(triangulate_expolygon_2f(logo_poly, NORMALS_UP), GROUND_Z+0.02f)) + m_logo_triangles.reset(); + if (!init_model_from_poly(m_logo_triangles, logo_poly, GROUND_Z + 0.02f)) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create logo triangles\n"; - else { - ; - } ExPolygon poly; /*for (const Vec2d& p : m_shape) { @@ -2511,6 +2412,7 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, Ve }*/ generate_print_polygon(poly); calc_triangles(poly); + init_raycaster_from_model(m_triangles); ExPolygon exclude_poly; /*for (const Vec2d& p : m_exclude_area) { @@ -2525,10 +2427,10 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, Ve //calc_vertex_for_icons_background(5, m_del_and_background_icon); //calc_vertex_for_icons(4, m_del_icon); calc_vertex_for_icons(0, m_del_icon); - calc_vertex_for_icons(1, m_orient_icon); - calc_vertex_for_icons(2, m_arrange_icon); - calc_vertex_for_icons(3, m_lock_icon); - calc_vertex_for_icons(4, m_plate_settings_icon); + calc_vertex_for_icons(1, m_orient_icon); + calc_vertex_for_icons(2, m_arrange_icon); + calc_vertex_for_icons(3, m_lock_icon); + calc_vertex_for_icons(4, m_plate_settings_icon); //calc_vertex_for_number(0, (m_plate_index < 9), m_plate_idx_icon); calc_vertex_for_number(0, false, m_plate_idx_icon); // calc vertex for plate name @@ -2537,8 +2439,6 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, Ve calc_height_limit(); - release_opengl_resource(); - return true; } @@ -2583,42 +2483,52 @@ bool PartPlate::intersects(const BoundingBoxf3& bb) const return print_volume.intersects(bb); } -void PartPlate::render(bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id, bool render_cali) +void PartPlate::render(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_body, bool force_background_color, HeightLimitMode mode, int hover_id, bool render_cali) { - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnable(GL_DEPTH_TEST)); - if (!bottom) { - // draw background - render_background(force_background_color); + GLShaderProgram *shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - render_exclude_area(force_background_color); - } + shader->set_uniform("view_model_matrix", view_matrix); + shader->set_uniform("projection_matrix", projection_matrix); - render_grid(bottom); + if (!bottom) { + // draw background + render_background(force_background_color); - if (!bottom && m_selected && !force_background_color) { - if (m_partplate_list) - render_logo(bottom, m_partplate_list->render_cali_logo && render_cali); - else - render_logo(bottom); - } + render_exclude_area(force_background_color); + } - render_height_limit(mode); + render_grid(bottom); - render_icons(bottom, only_body, hover_id); - if (!force_background_color){ - render_only_numbers(bottom); - } - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - glsafe(::glDisable(GL_BLEND)); + render_height_limit(mode); - //if (with_label) { - // render_label(canvas); - //} - glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + + // if (with_label) { + // render_label(canvas); + // } + + shader->stop_using(); + } + + if (!bottom && m_selected && !force_background_color) { + if (m_partplate_list) + render_logo(bottom, m_partplate_list->render_cali_logo && render_cali); + else + render_logo(bottom); + } + + render_icons(bottom, only_body, hover_id); + if (!force_background_color) { + render_only_numbers(bottom); + } + + glsafe(::glDisable(GL_DEPTH_TEST)); } void PartPlate::set_selected() { @@ -3180,10 +3090,6 @@ void PartPlateList::release_icon_textures() part.texture->reset(); delete part.texture; } - if (part.vbo_id != 0) { - glsafe(::glDeleteBuffers(1, &part.vbo_id)); - part.vbo_id = 0; - } if (part.buffer) { delete part.buffer; } @@ -4437,7 +4343,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr /*rendering related functions*/ //render -void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) +void PartPlateList::render(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) { const std::lock_guard local_lock(m_plates_mutex); std::vector::iterator it = m_plate_list.begin(); @@ -4462,28 +4368,19 @@ void PartPlateList::render(bool bottom, bool only_current, bool only_body, int h if (current_index == m_current_plate) { PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode; if (plate_hover_index == current_index) - (*it)->render(bottom, only_body, false, height_mode, plate_hover_action, render_cali); + (*it)->render(view_matrix, projection_matrix, bottom, only_body, false, height_mode, plate_hover_action, render_cali); else - (*it)->render(bottom, only_body, false, height_mode, -1, render_cali); + (*it)->render(view_matrix, projection_matrix, bottom, only_body, false, height_mode, -1, render_cali); } else { if (plate_hover_index == current_index) - (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action, render_cali); + (*it)->render(view_matrix, projection_matrix, bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, plate_hover_action, render_cali); else - (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali); + (*it)->render(view_matrix, projection_matrix, bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali); } } } -void PartPlateList::render_for_picking_pass() -{ - const std::lock_guard local_lock(m_plates_mutex); - std::vector::iterator it = m_plate_list.begin(); - for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { - (*it)->render_for_picking(); - } -} - /*int PartPlateList::select_plate_by_hover_id(int hover_id) { int index = hover_id / PartPlate::GRABBER_COUNT; @@ -5096,19 +4993,11 @@ void PartPlateList::BedTextureInfo::TexturePart::update_buffer() } if (!buffer) - buffer = new GeometryBuffer(); + buffer = new GLModel(); - if (buffer->set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z + 0.02f)) { - if (vbo_id != 0) { - glsafe(::glDeleteBuffers(1, &vbo_id)); - vbo_id = 0; - } - unsigned int* vbo_id_ptr = const_cast(&vbo_id); - glsafe(::glGenBuffers(1, vbo_id_ptr)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id_ptr)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)buffer->get_vertices_data_size(), (const GLvoid*)buffer->get_vertices_data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } else { + buffer->reset(); + + if (!init_model_from_poly(*buffer, poly, GROUND_Z + 0.02f)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create buffer triangles\n"; } } diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index e2b2a3bf1d..ced713a1f0 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -21,6 +21,7 @@ #include "3DScene.hpp" #include "GLModel.hpp" #include "3DBed.hpp" +#include "MeshUtils.hpp" class GLUquadric; typedef class GLUquadric GLUquadricObject; @@ -115,31 +116,22 @@ private: Transform3d m_grabber_trans_matrix; Slic3r::Geometry::Transformation position; std::vector positions; - unsigned int m_vbo_id{ 0 }; - GeometryBuffer m_triangles; - GeometryBuffer m_exclude_triangles; - GeometryBuffer m_logo_triangles; - GeometryBuffer m_gridlines; - GeometryBuffer m_gridlines_bolder; - GeometryBuffer m_height_limit_common; - GeometryBuffer m_height_limit_bottom; - GeometryBuffer m_height_limit_top; - GeometryBuffer m_del_icon; - //GeometryBuffer m_del_and_background_icon; - mutable unsigned int m_del_vbo_id{ 0 }; - GeometryBuffer m_arrange_icon; - mutable unsigned int m_arrange_vbo_id{ 0 }; - GeometryBuffer m_orient_icon; - mutable unsigned int m_orient_vbo_id{ 0 }; - GeometryBuffer m_lock_icon; - mutable unsigned int m_lock_vbo_id{ 0 }; - GeometryBuffer m_plate_settings_icon; - mutable unsigned int m_plate_settings_vbo_id{ 0 }; - GeometryBuffer m_plate_idx_icon; - mutable unsigned int m_plate_idx_vbo_id{ 0 }; + PickingModel m_triangles; + GLModel m_exclude_triangles; + GLModel m_logo_triangles; + GLModel m_gridlines; + GLModel m_gridlines_bolder; + GLModel m_height_limit_common; + GLModel m_height_limit_bottom; + GLModel m_height_limit_top; + PickingModel m_del_icon; + PickingModel m_arrange_icon; + PickingModel m_orient_icon; + PickingModel m_lock_icon; + PickingModel m_plate_settings_icon; + GLModel m_plate_idx_icon; GLTexture m_texture; - mutable float m_grabber_color[4]; float m_scale_factor{ 1.0f }; GLUquadricObject* m_quadric; int m_hover_id; @@ -152,8 +144,7 @@ private: // SoftFever // part plate name std::string m_name; - GeometryBuffer m_plate_name_icon; - mutable unsigned int m_plate_name_vbo_id{ 0 }; + GLModel m_plate_name_icon; GLTexture m_name_texture; wxCoord m_name_texture_width; wxCoord m_name_texture_height; @@ -168,48 +159,46 @@ private: void calc_exclude_triangles(const ExPolygon& poly); void calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox); void calc_height_limit(); - void calc_vertex_for_number(int index, bool one_number, GeometryBuffer &buffer); - void calc_vertex_for_icons(int index, GeometryBuffer &buffer); - void calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer); - void render_background(bool force_default_color = false) const; - void render_logo(bool bottom, bool render_cali = true) const; - void render_logo_texture(GLTexture& logo_texture, const GeometryBuffer& logo_buffer, bool bottom, unsigned int vbo_id) const; - void render_exclude_area(bool force_default_color) const; - //void render_background_for_picking(const float* render_color) const; - void render_grid(bool bottom) const; - void render_height_limit(PartPlate::HeightLimitMode mode = HEIGHT_LIMIT_BOTH) const; - void render_label(GLCanvas3D& canvas) const; - void render_grabber(const float* render_color, bool use_lighting) const; - void render_face(float x_size, float y_size) const; - void render_arrows(const float* render_color, bool use_lighting) const; - void render_left_arrow(const float* render_color, bool use_lighting) const; - void render_right_arrow(const float* render_color, bool use_lighting) const; - void render_icon_texture(int position_id, int tex_coords_id, const GeometryBuffer &buffer, GLTexture &texture, unsigned int &vbo_id) const; + void calc_vertex_for_number(int index, bool one_number, GLModel &buffer); + void calc_vertex_for_icons(int index, PickingModel &model); + // void calc_vertex_for_icons_background(int icon_count, GLModel &buffer); + void render_background(bool force_default_color = false); + void render_logo(bool bottom, bool render_cali = true); + void render_logo_texture(GLTexture &logo_texture, GLModel &logo_buffer, bool bottom); + void render_exclude_area(bool force_default_color); + //void render_background_for_picking(const ColorRGBA render_color) const; + void render_grid(bool bottom); + void render_height_limit(PartPlate::HeightLimitMode mode = HEIGHT_LIMIT_BOTH); + // void render_label(GLCanvas3D& canvas) const; + // void render_grabber(const ColorRGBA render_color, bool use_lighting) const; + // void render_face(float x_size, float y_size) const; + // void render_arrows(const ColorRGBA render_color, bool use_lighting) const; + // void render_left_arrow(const ColorRGBA render_color, bool use_lighting) const; + // void render_right_arrow(const ColorRGBA render_color, bool use_lighting) const; + void render_icon_texture(GLModel &buffer, GLTexture &texture); void show_tooltip(const std::string tooltip); void render_icons(bool bottom, bool only_name = false, int hover_id = -1); - void render_only_numbers(bool bottom) const; - void render_plate_name_texture(int position_id, int tex_coords_id); - void render_rectangle_for_picking(const GeometryBuffer &buffer, const float* render_color) const; - void on_render_for_picking() const; - std::array picking_color_component(int idx) const; - void release_opengl_resource(); + void render_only_numbers(bool bottom); + void render_plate_name_texture(); + void register_raycasters_for_picking(GLCanvas3D& canvas); + int picking_id_component(int idx) const; public: static const unsigned int PLATE_BASE_ID = 255 * 255 * 253; static const unsigned int PLATE_NAME_HOVER_ID = 6; static const unsigned int GRABBER_COUNT = 7; - static std::array SELECT_COLOR; - static std::array UNSELECT_COLOR; - static std::array UNSELECT_DARK_COLOR; - static std::array DEFAULT_COLOR; - static std::array LINE_BOTTOM_COLOR; - static std::array LINE_TOP_COLOR; - static std::array LINE_TOP_DARK_COLOR; - static std::array LINE_TOP_SEL_COLOR; - static std::array LINE_TOP_SEL_DARK_COLOR; - static std::array HEIGHT_LIMIT_BOTTOM_COLOR; - static std::array HEIGHT_LIMIT_TOP_COLOR; + static ColorRGBA SELECT_COLOR; + static ColorRGBA UNSELECT_COLOR; + static ColorRGBA UNSELECT_DARK_COLOR; + static ColorRGBA DEFAULT_COLOR; + static ColorRGBA LINE_BOTTOM_COLOR; + static ColorRGBA LINE_TOP_COLOR; + static ColorRGBA LINE_TOP_DARK_COLOR; + static ColorRGBA LINE_TOP_SEL_COLOR; + static ColorRGBA LINE_TOP_SEL_DARK_COLOR; + static ColorRGBA HEIGHT_LIMIT_BOTTOM_COLOR; + static ColorRGBA HEIGHT_LIMIT_TOP_COLOR; static void update_render_colors(); static void load_render_colors(); @@ -350,8 +339,8 @@ public: bool contains(const BoundingBoxf3& bb) const; bool intersects(const BoundingBoxf3& bb) const; - void render(bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1, bool render_cali = false); - void render_for_picking() const { on_render_for_picking(); } + void render(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_body = false, bool force_background_color = false, HeightLimitMode mode = HEIGHT_LIMIT_NONE, int hover_id = -1, bool render_cali = false); + void set_selected(); void set_unselected(); void set_hover_id(int id) { m_hover_id = id; } @@ -569,18 +558,16 @@ public: float y; float w; float h; - unsigned int vbo_id; std::string filename; GLTexture* texture { nullptr }; Vec2d offset; - GeometryBuffer* buffer { nullptr }; + GLModel* buffer { nullptr }; TexturePart(float xx, float yy, float ww, float hh, std::string file){ x = xx; y = yy; w = ww; h = hh; filename = file; texture = nullptr; buffer = nullptr; - vbo_id = 0; offset = Vec2d(0, 0); } @@ -593,7 +580,6 @@ public: this->buffer = part.buffer; this->filename = part.filename; this->texture = part.texture; - this->vbo_id = part.vbo_id; } void update_buffer(); @@ -752,10 +738,14 @@ public: /*rendering related functions*/ void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; } - void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false); - void render_for_picking_pass(); + void render(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false); void set_render_option(bool bedtype_texture, bool plate_settings); void set_render_cali(bool value = true) { render_cali_logo = value; } + void register_raycasters_for_picking(GLCanvas3D& canvas) + { + for (auto plate : m_plate_list) + plate->register_raycasters_for_picking(canvas); + } BoundingBoxf3& get_bounding_box() { return m_bounding_box; } //int select_plate_by_hover_id(int hover_id); int select_plate_by_obj(int obj_index, int instance_index); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 553aecb1ab..5c8881f5b1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1,3 +1,22 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2022 Michael Kirsch +///|/ Copyright (c) 2021 Boleslaw Ciesielski +///|/ Copyright (c) 2019 John Drake @foxox +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Plater.hpp" #include "libslic3r/Config.hpp" @@ -128,6 +147,7 @@ #include "Gizmos/GLGizmosManager.hpp" #endif // __APPLE__ +#include #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" @@ -4039,7 +4059,7 @@ int Plater::priv::get_selected_volume_idx() const int idx = selection.get_object_idx(); if ((0 > idx) || (idx > 1000)) return-1; - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* v = selection.get_first_volume(); if (model.objects[idx]->volumes.size() > 1) return v->volume_idx(); return -1; @@ -4850,7 +4870,7 @@ void Plater::priv::replace_with_stl() if (selection.is_wipe_tower() || get_selection().get_volume_idxs().size() != 1) return; - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* v = selection.get_first_volume(); int object_idx = v->object_idx(); int volume_idx = v->volume_idx(); @@ -7160,7 +7180,7 @@ bool Plater::priv::can_edit_text() const return true; if (selection.is_single_volume()) { - const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume *gl_volume = selection.get_first_volume(); int out_object_idx = gl_volume->object_idx(); ModelObject * model_object = selection.get_model()->objects[out_object_idx]; int out_volume_idx = gl_volume->volume_idx(); @@ -8279,14 +8299,6 @@ void Plater::add_model(bool imperial_units, std::string fname) wxGetApp().mainframe->update_title(); } } -std::array get_cut_plane(const BoundingBoxf3& bbox, const double& cut_height) { - std::array plane_pts; - plane_pts[0] = Vec3d(bbox.min(0), bbox.min(1), cut_height); - plane_pts[1] = Vec3d(bbox.max(0), bbox.min(1), cut_height); - plane_pts[2] = Vec3d(bbox.max(0), bbox.max(1), cut_height); - plane_pts[3] = Vec3d(bbox.min(0), bbox.max(1), cut_height); - return plane_pts; -} void Plater::calib_pa(const Calib_Params& params) { @@ -8409,6 +8421,25 @@ void Plater::_calib_pa_pattern(const Calib_Params& params) changed_objects({ 0 }); } +void Plater::cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes) +{ + wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); + auto *object = p->model.objects[obj_idx]; + + wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); + + if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) + return; + + wxBusyCursor wait; + + const Vec3d instance_offset = object->instances[instance_idx]->get_offset(); + Cut cut(object, instance_idx, Geometry::translation_transform(z * Vec3d::UnitZ() - instance_offset), attributes); + const auto new_objects = cut.perform_with_plane(); + + apply_cut_object_to_model(obj_idx, new_objects); +} + void Plater::_calib_pa_tower(const Calib_Params& params) { add_model(false, Slic3r::resources_dir() + "/calib/pressure_advance/tower_with_seam.stl"); @@ -8445,8 +8476,7 @@ void Plater::_calib_pa_tower(const Calib_Params& params) { auto new_height = std::ceil((params.end - params.start) / params.step) + 1; auto obj_bb = model().objects[0]->bounding_box(); if (new_height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, new_height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower); } _calib_pa_select_added_objects(); @@ -8587,8 +8617,7 @@ void Plater::calib_temp(const Calib_Params& params) { // add EPSILON offset to avoid cutting at the exact location where the flat surface is auto new_height = block_count * 10.0 + EPSILON; if (new_height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, new_height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower); } } @@ -8598,8 +8627,7 @@ void Plater::calib_temp(const Calib_Params& params) { if(block_count > 0){ auto new_height = block_count * 10.0 + EPSILON; if (new_height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, new_height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepUpper); + cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepUpper); } } @@ -8667,8 +8695,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params) auto obj_bb = obj->bounding_box(); auto height = (params.end - params.start + 1) / params.step; if (height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower); } auto new_params = params; @@ -8716,8 +8743,7 @@ void Plater::calib_retraction(const Calib_Params& params) auto obj_bb = obj->bounding_box(); auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step; if (height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower); } p->background_process.fff_print()->set_calib_params(params); @@ -8758,8 +8784,7 @@ void Plater::calib_VFA(const Calib_Params& params) auto obj_bb = model().objects[0]->bounding_box(); auto height = 5 * ((params.end - params.start) / params.step + 1); if (height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane(obj_bb, height); - cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower); } p->background_process.fff_print()->set_calib_params(params); @@ -9864,27 +9889,16 @@ void Plater::convert_unit(ConversionType conv_type) } } -// BBS: replace z with plane_points -void Plater::cut(size_t obj_idx, size_t instance_idx, std::array plane_points, ModelObjectCutAttributes attributes) +void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects) { - wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); - auto *object = p->model.objects[obj_idx]; + model().delete_object(obj_idx); + sidebar().obj_list()->delete_object_from_list(obj_idx); - wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - - if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) - return; - - wxBusyCursor wait; - // BBS: replace z with plane_points - const auto new_objects = object->cut(instance_idx, plane_points, attributes); - - remove(obj_idx); - p->load_model_objects(new_objects); + // suppress to call selection update for Object List to avoid call of early Gizmos on/off update + p->load_model_objects(new_objects, false, false); // now process all updates of the 3d scene update(); - // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call for (size_t idx = 0; idx < p->model.objects.size(); idx++) @@ -9894,62 +9908,10 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, std::array plane size_t last_id = p->model.objects.size() - 1; for (size_t i = 0; i < new_objects.size(); ++i) selection.add_object((unsigned int)(last_id - i), i == 0); -} -// BBS -void Plater::segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha, int segment_number) -{ - wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); - auto* object = p->model.objects[obj_idx]; - - wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - - Plater::TakeSnapshot snapshot(this, "Segment"); - - wxBusyCursor wait; - // real process - PresetBundle& preset_bundle = *wxGetApp().preset_bundle; - const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); - const size_t filament_cnt = print_tech != ptFFF ? 1 : preset_bundle.filament_presets.size(); - const auto new_objects = object->segment(instance_idx, filament_cnt, smoothing_alpha, segment_number); - - remove(obj_idx); - p->load_model_objects(new_objects); - - Selection& selection = p->get_selection(); - size_t last_id = p->model.objects.size() - 1; - for (size_t i = 0; i < new_objects.size(); ++i) - { - selection.add_object((unsigned int)(last_id - i), i == 0); - } -} - -// BBS -void Plater::merge(size_t obj_idx, std::vector& vol_indeces) -{ - wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); - auto* object = p->model.objects[obj_idx]; - - Plater::TakeSnapshot snapshot(this, "Merge"); - - wxBusyCursor wait; - // real process - PresetBundle& preset_bundle = *wxGetApp().preset_bundle; - const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); - // BBS - const size_t filament_cnt = print_tech != ptFFF ? 1 : preset_bundle.filament_presets.size(); - - const auto new_objects = object->merge_volumes(vol_indeces); - - remove(obj_idx); - p->load_model_objects(new_objects); - - Selection& selection = p->get_selection(); - size_t last_id = p->model.objects.size() - 1; - for (size_t i = 0; i < new_objects.size(); ++i) - { - selection.add_object((unsigned int)(last_id - i), i == 0); - } + // UIThreadWorker w; + // arrange(w, true); + // w.wait_for_idle(); } void Plater::export_gcode(bool prefer_removable) @@ -10340,7 +10302,7 @@ void Plater::export_stl(bool extended, bool selection_only) if (selection.get_mode() == Selection::Instance) mesh = mesh_to_export(*model_object, (model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()); else { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume* volume = selection.get_first_volume(); mesh = model_object->volumes[volume->volume_idx()]->mesh(); mesh.transform(volume->get_volume_transformation().get_matrix(), true); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 62c505f5ad..809a3ed801 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -1,3 +1,19 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_Plater_hpp_ #define slic3r_Plater_hpp_ @@ -25,6 +41,7 @@ #include "libslic3r/PrintBase.hpp" #include "libslic3r/calib.hpp" +#include "libslic3r/CutUtils.hpp" #define FILAMENT_SYSTEM_COLORS_NUM 16 @@ -41,8 +58,6 @@ class BuildVolume; enum class BuildVolume_Type : unsigned char; class Model; class ModelObject; -enum class ModelObjectCutAttribute : int; -using ModelObjectCutAttributes = enum_bitmask; class ModelInstance; class Print; class SLAPrint; @@ -323,12 +338,7 @@ public: void scale_selection_to_fit_print_volume(); void convert_unit(ConversionType conv_type); - // BBS: replace z with plane_points - void cut(size_t obj_idx, size_t instance_idx, std::array plane_points, ModelObjectCutAttributes attributes); - - // BBS: segment model with CGAL - void segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha=0.5, int segment_number=5); - void merge(size_t obj_idx, std::vector& vol_indeces); + void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); void send_to_printer(bool isall = false); void export_gcode(bool prefer_removable); @@ -742,6 +752,8 @@ private: void _calib_pa_tower(const Calib_Params& params); void _calib_pa_select_added_objects(); + void cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes); + friend class SuppressBackgroundProcessingUpdate; }; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index f58b759d84..dec7c525be 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -24,6 +24,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "GUI_App.hpp" diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp new file mode 100644 index 0000000000..96be46de29 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -0,0 +1,318 @@ +///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "libslic3r/libslic3r.h" +#include "SceneRaycaster.hpp" + +#include "Camera.hpp" +#include "GUI_App.hpp" +#include "Selection.hpp" +#include "Plater.hpp" + +namespace Slic3r { +namespace GUI { + +SceneRaycaster::SceneRaycaster() { +#if ENABLE_RAYCAST_PICKING_DEBUG + // hit point + m_sphere.init_from(its_make_sphere(1.0, double(PI) / 16.0)); + m_sphere.set_color(ColorRGBA::YELLOW()); + + // hit normal + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::YELLOW(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)Vec3f::Zero()); + init_data.add_vertex((Vec3f)Vec3f::UnitZ()); + + // indices + init_data.add_line(0, 1); + + m_line.init_from(std::move(init_data)); +#endif // ENABLE_RAYCAST_PICKING_DEBUG +} + +std::shared_ptr SceneRaycaster::add_raycaster(EType type, int id, const MeshRaycaster& raycaster, + const Transform3d& trafo, bool use_back_faces) +{ + switch (type) { + case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::FallbackGizmo: { return m_fallback_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + default: { assert(false); return nullptr; } + }; +} + +void SceneRaycaster::remove_raycasters(EType type, int id) +{ + std::vector>* raycasters = get_raycasters(type); + auto it = raycasters->begin(); + while (it != raycasters->end()) { + if ((*it)->get_id() == encode_id(type, id)) + it = raycasters->erase(it); + else + ++it; + } +} + +void SceneRaycaster::remove_raycasters(EType type) +{ + switch (type) { + case EType::Bed: { m_bed.clear(); break; } + case EType::Volume: { m_volumes.clear(); break; } + case EType::Gizmo: { m_gizmos.clear(); break; } + case EType::FallbackGizmo: { m_fallback_gizmos.clear(); break; } + default: { break; } + }; +} + +void SceneRaycaster::remove_raycaster(std::shared_ptr item) +{ + for (auto it = m_bed.begin(); it != m_bed.end(); ++it) { + if (*it == item) { + m_bed.erase(it); + return; + } + } + for (auto it = m_volumes.begin(); it != m_volumes.end(); ++it) { + if (*it == item) { + m_volumes.erase(it); + return; + } + } + for (auto it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { + if (*it == item) { + m_gizmos.erase(it); + return; + } + } + for (auto it = m_fallback_gizmos.begin(); it != m_fallback_gizmos.end(); ++it) { + if (*it == item) { + m_fallback_gizmos.erase(it); + return; + } + } +} + +SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const +{ + // helper class used to return currently selected volume as hit when overlapping with other volumes + // to allow the user to click and drag on a selected volume + class VolumeKeeper + { + std::optional m_selected_volume_id; + Vec3f m_closest_hit_pos{ std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + bool m_selected_volume_already_found{ false }; + + public: + VolumeKeeper() { + const Selection& selection = wxGetApp().plater()->get_selection(); + if (selection.is_single_volume() || selection.is_single_modifier()) { + const GLVolume* volume = selection.get_first_volume(); + if (!volume->is_wipe_tower && !volume->is_sla_pad() && !volume->is_sla_support()) + m_selected_volume_id = *selection.get_volume_idxs().begin(); + } + } + + bool is_active() const { return m_selected_volume_id.has_value(); } + const Vec3f& get_closest_hit_pos() const { return m_closest_hit_pos; } + bool check_hit_result(const HitResult& hit) { + assert(is_active()); + + if (m_selected_volume_already_found && hit.type == SceneRaycaster::EType::Volume && hit.position.isApprox(m_closest_hit_pos)) + return false; + + if (hit.type == SceneRaycaster::EType::Volume) + m_selected_volume_already_found = *m_selected_volume_id == (unsigned int)decode_id(hit.type, hit.raycaster_id); + + m_closest_hit_pos = hit.position; + return true; + } + }; + + VolumeKeeper volume_keeper; + + double closest_hit_squared_distance = std::numeric_limits::max(); + auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) { + const double hit_squared_distance = (camera.get_position() - hit.cast()).squaredNorm(); + bool ret = hit_squared_distance < closest_hit_squared_distance; + if (volume_keeper.is_active()) + ret |= hit.isApprox(volume_keeper.get_closest_hit_pos()); + if (ret) + closest_hit_squared_distance = hit_squared_distance; + return ret; + }; + +#if ENABLE_RAYCAST_PICKING_DEBUG + const_cast*>(&m_last_hit)->reset(); +#endif // ENABLE_RAYCAST_PICKING_DEBUG + + HitResult ret; + + auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { + const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; + const std::vector>* raycasters = get_raycasters(type); + const Vec3f camera_forward = camera.get_dir_forward().cast(); + HitResult current_hit = { type }; + for (std::shared_ptr item : *raycasters) { + if (!item->is_active()) + continue; + + current_hit.raycaster_id = item->get_id(); + const Transform3d& trafo = item->get_transform(); + if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { + current_hit.position = (trafo * current_hit.position.cast()).cast(); + current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast()).normalized().cast(); + if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) { + if (is_closest(camera, current_hit.position)) { + if (volume_keeper.is_active()) { + if (volume_keeper.check_hit_result(current_hit)) + ret = current_hit; + } + else + ret = current_hit; + } + } + } + } + }; + + if (!m_gizmos.empty()) + test_raycasters(EType::Gizmo, mouse_pos, camera, ret); + + if (!m_fallback_gizmos.empty() && !ret.is_valid()) + test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret); + + if (!m_gizmos_on_top || !ret.is_valid()) { + if (camera.is_looking_downward() && !m_bed.empty()) + test_raycasters(EType::Bed, mouse_pos, camera, ret); + if (!m_volumes.empty()) + test_raycasters(EType::Volume, mouse_pos, camera, ret); + } + + if (ret.is_valid()) + ret.raycaster_id = decode_id(ret.type, ret.raycaster_id); + +#if ENABLE_RAYCAST_PICKING_DEBUG + *const_cast*>(&m_last_hit) = ret; +#endif // ENABLE_RAYCAST_PICKING_DEBUG + return ret; +} + +#if ENABLE_RAYCAST_PICKING_DEBUG +void SceneRaycaster::render_hit(const Camera& camera) +{ + if (!m_last_hit.has_value() || !(*m_last_hit).is_valid()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + shader->start_using(); + + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const Transform3d sphere_view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform((*m_last_hit).position.cast()) * + Geometry::scale_transform(4.0 * camera.get_inv_zoom()); + shader->set_uniform("view_model_matrix", sphere_view_model_matrix); + m_sphere.render(); + + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), (*m_last_hit).normal.cast()).toRotationMatrix(); + + const Transform3d line_view_model_matrix = sphere_view_model_matrix * m * Geometry::scale_transform(10.0); + shader->set_uniform("view_model_matrix", line_view_model_matrix); + m_line.render(); + + shader->stop_using(); +} + +size_t SceneRaycaster::active_beds_count() const { + size_t count = 0; + for (const auto& b : m_bed) { + if (b->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_volumes_count() const { + size_t count = 0; + for (const auto& v : m_volumes) { + if (v->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_fallback_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_fallback_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} +#endif // ENABLE_RAYCAST_PICKING_DEBUG + +std::vector>* SceneRaycaster::get_raycasters(EType type) +{ + std::vector>* ret = nullptr; + switch (type) + { + case EType::Bed: { ret = &m_bed; break; } + case EType::Volume: { ret = &m_volumes; break; } + case EType::Gizmo: { ret = &m_gizmos; break; } + case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; } + default: { break; } + } + assert(ret != nullptr); + return ret; +} + +const std::vector>* SceneRaycaster::get_raycasters(EType type) const +{ + const std::vector>* ret = nullptr; + switch (type) + { + case EType::Bed: { ret = &m_bed; break; } + case EType::Volume: { ret = &m_volumes; break; } + case EType::Gizmo: { ret = &m_gizmos; break; } + case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; } + default: { break; } + } + assert(ret != nullptr); + return ret; +} + +int SceneRaycaster::base_id(EType type) +{ + switch (type) + { + case EType::Bed: { return int(EIdBase::Bed); } + case EType::Volume: { return int(EIdBase::Volume); } + case EType::Gizmo: { return int(EIdBase::Gizmo); } + case EType::FallbackGizmo: { return int(EIdBase::FallbackGizmo); } + default: { break; } + }; + + assert(false); + return -1; +} + +int SceneRaycaster::encode_id(EType type, int id) { return base_id(type) + id; } +int SceneRaycaster::decode_id(EType type, int id) { return id - base_id(type); } + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp new file mode 100644 index 0000000000..778ec0ab43 --- /dev/null +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -0,0 +1,126 @@ +///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_SceneRaycaster_hpp_ +#define slic3r_SceneRaycaster_hpp_ + +#include "MeshUtils.hpp" +#include "GLModel.hpp" +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +struct Camera; + +class SceneRaycasterItem +{ + int m_id{ -1 }; + bool m_active{ true }; + bool m_use_back_faces{ false }; + const MeshRaycaster* m_raycaster; + Transform3d m_trafo; + +public: + SceneRaycasterItem(int id, const MeshRaycaster& raycaster, const Transform3d& trafo, bool use_back_faces = false) + : m_id(id), m_raycaster(&raycaster), m_trafo(trafo), m_use_back_faces(use_back_faces) + {} + + int get_id() const { return m_id; } + bool is_active() const { return m_active; } + void set_active(bool active) { m_active = active; } + bool use_back_faces() const { return m_use_back_faces; } + const MeshRaycaster* get_raycaster() const { return m_raycaster; } + const Transform3d& get_transform() const { return m_trafo; } + void set_transform(const Transform3d& trafo) { m_trafo = trafo; } +}; + +class SceneRaycaster +{ +public: + enum class EType + { + None, + Bed, + Volume, + Gizmo, + FallbackGizmo // Is used for gizmo grabbers which will be hit after all grabbers of Gizmo type + }; + + enum class EIdBase + { + Bed = 0, + Volume = 1000, + Gizmo = 1000000, + FallbackGizmo = 2000000 + }; + + struct HitResult + { + EType type{ EType::None }; + int raycaster_id{ -1 }; + Vec3f position{ Vec3f::Zero() }; + Vec3f normal{ Vec3f::Zero() }; + + bool is_valid() const { return raycaster_id != -1; } + }; + +private: + std::vector> m_bed; + std::vector> m_volumes; + std::vector> m_gizmos; + std::vector> m_fallback_gizmos; + + // When set to true, if checking gizmos returns a valid hit, + // the search is not performed on other types + bool m_gizmos_on_top{ false }; + +#if ENABLE_RAYCAST_PICKING_DEBUG + GLModel m_sphere; + GLModel m_line; + std::optional m_last_hit; +#endif // ENABLE_RAYCAST_PICKING_DEBUG + +public: + SceneRaycaster(); + + std::shared_ptr add_raycaster(EType type, int picking_id, const MeshRaycaster& raycaster, + const Transform3d& trafo, bool use_back_faces = false); + void remove_raycasters(EType type, int id); + void remove_raycasters(EType type); + void remove_raycaster(std::shared_ptr item); + + std::vector>* get_raycasters(EType type); + const std::vector>* get_raycasters(EType type) const; + + void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; } + + HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const; + +#if ENABLE_RAYCAST_PICKING_DEBUG + void render_hit(const Camera& camera); + + size_t beds_count() const { return m_bed.size(); } + size_t volumes_count() const { return m_volumes.size(); } + size_t gizmos_count() const { return m_gizmos.size(); } + size_t fallback_gizmos_count() const { return m_fallback_gizmos.size(); } + size_t active_beds_count() const; + size_t active_volumes_count() const; + size_t active_gizmos_count() const; + size_t active_fallback_gizmos_count() const; +#endif // ENABLE_RAYCAST_PICKING_DEBUG + + static int decode_id(EType type, int id); + +private: + static int encode_id(EType type, int id); + static int base_id(EType type); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_SceneRaycaster_hpp_ diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index f18c8a1600..0346a2093c 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -3,6 +3,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Thread.hpp" +#include "libslic3r/Color.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_Preview.hpp" @@ -3643,7 +3644,6 @@ void SelectMachineDialog::set_default_normal() //init MaterialItem auto extruders = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_used_extruders(); - BitmapCache bmcache; MaterialHash::iterator iter = m_materialList.begin(); while (iter != m_materialList.end()) { @@ -3661,10 +3661,10 @@ void SelectMachineDialog::set_default_normal() for (auto i = 0; i < extruders.size(); i++) { auto extruder = extruders[i] - 1; auto colour = wxGetApp().preset_bundle->project_config.opt_string("filament_colour", (unsigned int) extruder); - unsigned char rgb[4]; - bmcache.parse_color4(colour, rgb); + ColorRGBA rgb; + decode_color(colour, rgb); - auto colour_rgb = wxColour((int) rgb[0], (int) rgb[1], (int) rgb[2], (int) rgb[3]); + auto colour_rgb = wxColour((int) rgb.r_uchar(), (int) rgb.g_uchar(), (int) rgb.b_uchar(), (int) rgb.a_uchar()); if (extruder >= materials.size() || extruder < 0 || extruder >= display_materials.size()) continue; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c9e8fb9b4c..9e385ab54c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "Selection.hpp" @@ -23,7 +27,9 @@ #include #include -static const std::array UNIFORM_SCALE_COLOR = { 0.923f, 0.504f, 0.264f, 1.0f }; +static const Slic3r::ColorRGBA UNIFORM_SCALE_COLOR = Slic3r::ColorRGBA::ORANGE(); +static const Slic3r::ColorRGBA SOLID_PLANE_COLOR = {0.0f, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f}; +static const Slic3r::ColorRGBA TRANSPARENT_PLANE_COLOR = { 0.8f, 0.8f, 0.8f, 0.5f }; namespace Slic3r { namespace GUI { @@ -115,7 +121,6 @@ Selection::Selection() , m_type(Empty) , m_valid(false) , m_scale_factor(1.0f) - , m_dragging(false) { this->set_bounding_boxes_dirty(); } @@ -132,9 +137,8 @@ bool Selection::init() { m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); - #if ENABLE_RENDER_SELECTION_CENTER - m_vbo_sphere.init_from(make_sphere(0.75, 2*PI/24)); + m_vbo_sphere.init_from(its_make_sphere(0.75, PI / 12.0)); #endif // ENABLE_RENDER_SELECTION_CENTER return true; @@ -572,11 +576,11 @@ void Selection::clear() for (unsigned int i : m_list) { GLVolume& volume = *(*m_volumes)[i]; volume.selected = false; - bool transparent = volume.color[3] < 1.0f; - if (transparent) + bool is_transparent = volume.color.is_transparent(); + if (is_transparent) volume.force_transparent = true; volume.set_render_color(); - if (transparent) + if (is_transparent) volume.force_transparent = false; } #else @@ -640,6 +644,28 @@ void Selection::volumes_changed(const std::vector &map_volume_old_to_new this->set_bounding_boxes_dirty(); } +bool Selection::is_any_connector() const +{ + const int obj_idx = get_object_idx(); + + if ((is_any_volume() || is_any_modifier() || is_mixed()) && // some solid_part AND/OR modifier is selected + obj_idx >= 0 && m_model->objects[obj_idx]->is_cut()) { + const ModelVolumePtrs& obj_volumes = m_model->objects[obj_idx]->volumes; + for (size_t vol_idx = 0; vol_idx < obj_volumes.size(); vol_idx++) + if (obj_volumes[vol_idx]->is_cut_connector()) + for (const GLVolume* v : *m_volumes) + if (v->object_idx() == obj_idx && v->volume_idx() == (int)vol_idx && v->selected) + return true; + } + return false; +} + +bool Selection::is_any_cut_volume() const +{ + const int obj_idx = get_object_idx(); + return is_any_volume() && obj_idx >= 0 && m_model->objects[obj_idx]->is_cut(); +} + bool Selection::is_single_full_instance() const { if (m_type == SingleFullInstance) @@ -710,6 +736,17 @@ bool Selection::contains_any_volume(const std::vector& volume_idxs return false; } +bool Selection::contains_sinking_volumes(bool ignore_modifiers) const +{ + for (const GLVolume* v : *m_volumes) { + if (!ignore_modifiers || !v->is_modifier) { + if (v->is_sinking()) + return true; + } + } + return false; +} + bool Selection::matches(const std::vector& volume_idxs) const { unsigned int count = 0; @@ -811,12 +848,11 @@ const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const return *m_scaled_instance_bounding_box; } -void Selection::start_dragging() +void Selection::setup_cache() { if (!m_valid) return; - m_dragging = true; set_caches(); } @@ -1165,12 +1201,12 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume) type.set_joint(); // apply scale - start_dragging(); + setup_cache(); scale(s * Vec3d::Ones(), type); wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot // center selection on print bed - start_dragging(); + setup_cache(); offset.z() = -get_bounding_box().min.z(); translate(offset); wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot @@ -1552,66 +1588,69 @@ void Selection::erase() } } -void Selection::render(float scale_factor) const +void Selection::render(float scale_factor) { if (!m_valid || is_empty()) return; - *const_cast(&m_scale_factor) = scale_factor; - + m_scale_factor = scale_factor; // render cumulative bounding box of selected volumes - render_selected_volumes(); + render_bounding_box(get_bounding_box(), ColorRGB::WHITE()); render_synchronized_volumes(); } #if ENABLE_RENDER_SELECTION_CENTER -void Selection::render_center(bool gizmo_is_dragging) const +void Selection::render_center(bool gizmo_is_dragging) { if (!m_valid || is_empty()) return; + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glColor3f(1.0f, 1.0f, 1.0f)); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center(0), center(1), center(2))); + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(center); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + m_vbo_sphere.set_color(ColorRGBA::WHITE()); m_vbo_sphere.render(); - glsafe(::glPopMatrix()); + + shader->stop_using(); } #endif // ENABLE_RENDER_SELECTION_CENTER //BBS: GUI refactor, add uniform scale from gizmo -void Selection::render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale) const -//void Selection::render_sidebar_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale) +//void Selection::render_sidebar_hints(const std::string& sidebar_field) { if (sidebar_field.empty()) return; - GLShaderProgram* shader = nullptr; + GLShaderProgram* shader = wxGetApp().get_shader(boost::starts_with(sidebar_field, "layer") ? "flat" : "gouraud_light"); + if (shader == nullptr) + return; - if (!boost::starts_with(sidebar_field, "layer")) { - shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - } + shader->start_using(); glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); + const Transform3d base_matrix = Geometry::assemble_transform(get_bounding_box().center()); + Transform3d orient_matrix = Transform3d::Identity(); if (!boost::starts_with(sidebar_field, "layer")) { - const Vec3d& center = get_bounding_box().center(); - + shader->set_uniform("emission_factor", 0.05f); // BBS if (is_single_full_instance()/* && !wxGetApp().obj_manipul()->get_world_coordinates()*/) { - glsafe(::glTranslated(center(0), center(1), center(2))); if (!boost::starts_with(sidebar_field, "position")) { - Transform3d orient_matrix = Transform3d::Identity(); if (boost::starts_with(sidebar_field, "scale")) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); else if (boost::starts_with(sidebar_field, "rotation")) { @@ -1619,50 +1658,45 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, bool unif orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); else if (boost::ends_with(sidebar_field, "y")) { const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation(); - if (rotation(0) == 0.0) + if (rotation.x() == 0.0) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); else - orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); + orient_matrix.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ())); } } - - glsafe(::glMultMatrixd(orient_matrix.data())); } - } else if (is_single_volume() || is_single_modifier()) { - glsafe(::glTranslated(center(0), center(1), center(2))); - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + } + else if (is_single_volume() || is_single_modifier()) { + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); if (!boost::starts_with(sidebar_field, "position")) orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); - glsafe(::glMultMatrixd(orient_matrix.data())); - } else { - glsafe(::glTranslated(center(0), center(1), center(2))); - if (requires_local_axes()) { - const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - glsafe(::glMultMatrixd(orient_matrix.data())); - } + } + else { + if (requires_local_axes()) + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); } } + if (!boost::starts_with(sidebar_field, "layer")) + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); if (boost::starts_with(sidebar_field, "position")) - render_sidebar_position_hints(sidebar_field); + render_sidebar_position_hints(sidebar_field, *shader, base_matrix * orient_matrix); else if (boost::starts_with(sidebar_field, "rotation")) - render_sidebar_rotation_hints(sidebar_field); + render_sidebar_rotation_hints(sidebar_field, *shader, base_matrix * orient_matrix); else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) //BBS: GUI refactor: add uniform_scale from gizmo - render_sidebar_scale_hints(sidebar_field, uniform_scale); + render_sidebar_scale_hints(sidebar_field, uniform_scale, *shader, base_matrix * orient_matrix); else if (boost::starts_with(sidebar_field, "layer")) - render_sidebar_layers_hints(sidebar_field); + render_sidebar_layers_hints(sidebar_field, *shader); - glsafe(::glPopMatrix()); - if (!boost::starts_with(sidebar_field, "layer")) - shader->stop_using(); + shader->stop_using(); } bool Selection::requires_local_axes() const { - return (m_mode == Volume) && is_from_single_instance(); + return m_mode == Volume && is_from_single_instance(); } void Selection::cut_to_clipboard() @@ -1678,8 +1712,7 @@ void Selection::copy_to_clipboard() m_clipboard.reset(); - for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content) - { + for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content) { ModelObject* src_object = m_model->objects[object.first]; ModelObject* dst_object = m_clipboard.add_object(); dst_object->name = src_object->name; @@ -1692,26 +1725,22 @@ void Selection::copy_to_clipboard() dst_object->layer_height_profile.assign(src_object->layer_height_profile); dst_object->origin_translation = src_object->origin_translation; - for (int i : object.second) - { + for (int i : object.second) { dst_object->add_instance(*src_object->instances[i]); } - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { // Copy the ModelVolumes only for the selected GLVolumes of the 1st selected instance. const GLVolume* volume = (*m_volumes)[i]; - if ((volume->object_idx() == object.first) && (volume->instance_idx() == *object.second.begin())) - { + if (volume->object_idx() == object.first && volume->instance_idx() == *object.second.begin()) { int volume_idx = volume->volume_idx(); - if ((0 <= volume_idx) && (volume_idx < (int)src_object->volumes.size())) - { + if (0 <= volume_idx && volume_idx < (int)src_object->volumes.size()) { ModelVolume* src_volume = src_object->volumes[volume_idx]; ModelVolume* dst_volume = dst_object->add_volume(*src_volume); dst_volume->set_new_unique_id(); - } else { - assert(false); } + else + assert(false); } } } @@ -2131,182 +2160,234 @@ void Selection::do_remove_object(unsigned int object_idx) } } -void Selection::render_selected_volumes() const -{ - float color[3] = { 1.0f, 1.0f, 1.0f }; - render_bounding_box(get_bounding_box(), color); -} - -void Selection::render_synchronized_volumes() const +void Selection::render_synchronized_volumes() { if (m_mode == Instance) return; - float color[3] = { 1.0f, 1.0f, 0.0f }; - for (unsigned int i : m_list) { - const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); - int volume_idx = volume->volume_idx(); + const GLVolume& volume = *(*m_volumes)[i]; + int object_idx = volume.object_idx(); + int volume_idx = volume.volume_idx(); for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { if (i == j) continue; - const GLVolume* v = (*m_volumes)[j]; - if (v->object_idx() != object_idx || v->volume_idx() != volume_idx) + const GLVolume& v = *(*m_volumes)[j]; + if (v.object_idx() != object_idx || v.volume_idx() != volume_idx) continue; - render_bounding_box(v->transformed_convex_hull_bounding_box(), color); + render_bounding_box(v.transformed_convex_hull_bounding_box(), ColorRGB::YELLOW()); } } } -void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) const +void Selection::render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color) { - if (color == nullptr) - return; + const BoundingBoxf3& curr_box = m_box.get_bounding_box(); + if (!m_box.is_initialized() || !is_approx(box.min, curr_box.min) || !is_approx(box.max, curr_box.max)) { + m_box.reset(); - Vec3f b_min = box.min.cast(); - Vec3f b_max = box.max.cast(); - Vec3f size = 0.2f * box.size().cast(); + const Vec3f b_min = box.min.cast(); + const Vec3f b_max = box.max.cast(); + const Vec3f size = 0.2f * box.size().cast(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(48); + init_data.reserve_indices(48); + + // vertices + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_min.z() + size.z())); + + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_min.z() + size.z())); + + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_min.z() + size.z())); + + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_min.z() + size.z())); + + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y() + size.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_min.y(), b_max.z() - size.z())); + + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y() + size.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_min.y(), b_max.z() - size.z())); + + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x() - size.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y() - size.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_max.x(), b_max.y(), b_max.z() - size.z())); + + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x() + size.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y() - size.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z())); + init_data.add_vertex(Vec3f(b_min.x(), b_max.y(), b_max.z() - size.z())); + + // indices + for (unsigned short i = 0; i < 48; ++i) { + init_data.add_index(i); + } + + m_box.init_from(std::move(init_data)); + } glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glColor3fv(color)); glsafe(::glLineWidth(2.0f * m_scale_factor)); - ::glBegin(GL_LINES); + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2)); - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_min(2)); - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1), b_min(2) + size(2)); - - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_min(2)); - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_min(2)); - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1), b_min(2) + size(2)); - - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_min(2)); - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_min(2)); - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1), b_min(2) + size(2)); - - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_min(2)); - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_min(2)); - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1), b_min(2) + size(2)); - - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_max(2)); - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_max(2)); - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1), b_max(2) - size(2)); - - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_max(2)); - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_max(2)); - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1), b_max(2) - size(2)); - - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_max(2)); - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_max(2)); - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1), b_max(2) - size(2)); - - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_max(2)); - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); - - glsafe(::glEnd()); + shader->start_using(); + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + m_box.set_color(to_rgba(color)); + m_box.render(); + shader->stop_using(); } -static std::array get_color(Axis axis) +static ColorRGBA get_color(Axis axis) { - return { GLGizmoBase::AXES_COLOR[axis][0], - GLGizmoBase::AXES_COLOR[axis][1], - GLGizmoBase::AXES_COLOR[axis][2], - GLGizmoBase::AXES_COLOR[axis][3] }; -}; + return GLGizmoBase::AXES_COLOR[axis]; +} -void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader.set_uniform("projection_matrix", camera.get_projection_matrix()); + if (boost::ends_with(sidebar_field, "x")) { - glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); - const_cast(&m_arrow)->set_color(-1, get_color(X)); + const Transform3d model_matrix = matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ()); + shader.set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); + m_arrow.set_color(get_color(X)); m_arrow.render(); } else if (boost::ends_with(sidebar_field, "y")) { - const_cast(&m_arrow)->set_color(-1, get_color(Y)); + shader.set_uniform("view_model_matrix", view_matrix * matrix); + shader.set_uniform("view_normal_matrix", (Matrix3d)Matrix3d::Identity()); + m_arrow.set_color(get_color(Y)); m_arrow.render(); } else if (boost::ends_with(sidebar_field, "z")) { - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - const_cast(&m_arrow)->set_color(-1, get_color(Z)); + const Transform3d model_matrix = matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitX()); + shader.set_uniform("view_model_matrix", view_matrix * model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); + m_arrow.set_color(get_color(Z)); m_arrow.render(); } } -void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix) { - auto render_sidebar_rotation_hint = [this]() { + auto render_sidebar_rotation_hint = [this](GLShaderProgram& shader, const Transform3d& view_matrix, const Transform3d& model_matrix) { + shader.set_uniform("view_model_matrix", view_matrix * model_matrix); + Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); m_curved_arrow.render(); - glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); + const Transform3d matrix = model_matrix * Geometry::assemble_transform(Vec3d::Zero(), PI * Vec3d::UnitZ()); + shader.set_uniform("view_model_matrix", view_matrix * matrix); + view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); m_curved_arrow.render(); }; + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader.set_uniform("projection_matrix", camera.get_projection_matrix()); + if (boost::ends_with(sidebar_field, "x")) { - glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); - const_cast(&m_curved_arrow)->set_color(-1, get_color(X)); - render_sidebar_rotation_hint(); + m_curved_arrow.set_color(get_color(X)); + render_sidebar_rotation_hint(shader, view_matrix, matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitY())); } else if (boost::ends_with(sidebar_field, "y")) { - glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - const_cast(&m_curved_arrow)->set_color(-1, get_color(Y)); - render_sidebar_rotation_hint(); + m_curved_arrow.set_color(get_color(Y)); + render_sidebar_rotation_hint(shader, view_matrix, matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitX())); } else if (boost::ends_with(sidebar_field, "z")) { - const_cast(&m_curved_arrow)->set_color(-1, get_color(Z)); - render_sidebar_rotation_hint(); + m_curved_arrow.set_color(get_color(Z)); + render_sidebar_rotation_hint(shader, view_matrix, matrix); } } //BBS: GUI refactor: add gizmo uniform_scale -void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, bool gizmo_uniform_scale) const +void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, bool gizmo_uniform_scale, GLShaderProgram& shader, const Transform3d& matrix) { // BBS //bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); bool uniform_scale = requires_uniform_scale() || gizmo_uniform_scale; - auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis) { - const_cast(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis)); - GLShaderProgram* shader = wxGetApp().get_current_shader(); - if (shader != nullptr) - shader->set_uniform("emission_factor", 0.0f); - - glsafe(::glTranslated(0.0, 5.0, 0.0)); + auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis, GLShaderProgram& shader, const Transform3d& view_matrix, const Transform3d& model_matrix) { + m_arrow.set_color(uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis)); + Transform3d matrix = model_matrix * Geometry::assemble_transform(5.0 * Vec3d::UnitY()); + shader.set_uniform("view_model_matrix", view_matrix * matrix); + Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); m_arrow.render(); - glsafe(::glTranslated(0.0, -10.0, 0.0)); - glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); + matrix = model_matrix * Geometry::assemble_transform(-5.0 * Vec3d::UnitY(), PI * Vec3d::UnitZ()); + shader.set_uniform("view_model_matrix", view_matrix * matrix); + view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader.set_uniform("view_normal_matrix", view_normal_matrix); m_arrow.render(); }; + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + shader.set_uniform("projection_matrix", camera.get_projection_matrix()); + if (boost::ends_with(sidebar_field, "x") || uniform_scale) { - glsafe(::glPushMatrix()); - glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); - render_sidebar_scale_hint(X); - glsafe(::glPopMatrix()); + render_sidebar_scale_hint(X, shader, view_matrix, matrix * Geometry::assemble_transform(Vec3d::Zero(), -0.5 * PI * Vec3d::UnitZ())); } if (boost::ends_with(sidebar_field, "y") || uniform_scale) { - glsafe(::glPushMatrix()); - render_sidebar_scale_hint(Y); - glsafe(::glPopMatrix()); + render_sidebar_scale_hint(Y, shader, view_matrix, matrix); } if (boost::ends_with(sidebar_field, "z") || uniform_scale) { - glsafe(::glPushMatrix()); - glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); - render_sidebar_scale_hint(Z); - glsafe(::glPopMatrix()); + render_sidebar_scale_hint(Z, shader, view_matrix, matrix * Geometry::assemble_transform(Vec3d::Zero(), 0.5 * PI * Vec3d::UnitX())); } } -void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const +void Selection::render_sidebar_layers_hints(const std::string& sidebar_field, GLShaderProgram& shader) { - static const double Margin = 10.0; + static const float Margin = 10.0f; std::string field = sidebar_field; @@ -2315,7 +2396,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - double max_z = string_to_double_decimal_point(field.substr(pos + 1)); + const float max_z = float(string_to_double_decimal_point(field.substr(pos + 1))); // extract min_z field = field.substr(0, pos); @@ -2323,7 +2404,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - const double min_z = string_to_double_decimal_point(field.substr(pos + 1)); + const float min_z = float(string_to_double_decimal_point(field.substr(pos + 1))); // extract type field = field.substr(0, pos); @@ -2335,42 +2416,71 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co const BoundingBoxf3& box = get_bounding_box(); - const float min_x = box.min(0) - Margin; - const float max_x = box.max(0) + Margin; - const float min_y = box.min(1) - Margin; - const float max_y = box.max(1) + Margin; - // view dependend order of rendering to keep correct transparency - bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); + const bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); const float z1 = camera_on_top ? min_z : max_z; const float z2 = camera_on_top ? max_z : min_z; + const Vec3f p1 = { float(box.min.x()) - Margin, float(box.min.y()) - Margin, z1 }; + const Vec3f p2 = { float(box.max.x()) + Margin, float(box.max.y()) + Margin, z2 }; + glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - ::glBegin(GL_QUADS); - if ((camera_on_top && type == 1) || (!camera_on_top && type == 2)) - ::glColor4f(0.0f, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f); - else - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, z1); - ::glVertex3f(max_x, min_y, z1); - ::glVertex3f(max_x, max_y, z1); - ::glVertex3f(min_x, max_y, z1); - glsafe(::glEnd()); + if (!m_planes.models[0].is_initialized() || !is_approx(m_planes.check_points[0], p1)) { + m_planes.check_points[0] = p1; + m_planes.models[0].reset(); - ::glBegin(GL_QUADS); - if ((camera_on_top && type == 2) || (!camera_on_top && type == 1)) - ::glColor4f(0.0f, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f); - else - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, z2); - ::glVertex3f(max_x, min_y, z2); - ::glVertex3f(max_x, max_y, z2); - ::glVertex3f(min_x, max_y, z2); - glsafe(::glEnd()); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + + // vertices + init_data.add_vertex(Vec3f(p1.x(), p1.y(), z1)); + init_data.add_vertex(Vec3f(p2.x(), p1.y(), z1)); + init_data.add_vertex(Vec3f(p2.x(), p2.y(), z1)); + init_data.add_vertex(Vec3f(p1.x(), p2.y(), z1)); + + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + + m_planes.models[0].init_from(std::move(init_data)); + } + + if (!m_planes.models[1].is_initialized() || !is_approx(m_planes.check_points[1], p2)) { + m_planes.check_points[1] = p2; + m_planes.models[1].reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + + // vertices + init_data.add_vertex(Vec3f(p1.x(), p1.y(), z2)); + init_data.add_vertex(Vec3f(p2.x(), p1.y(), z2)); + init_data.add_vertex(Vec3f(p2.x(), p2.y(), z2)); + init_data.add_vertex(Vec3f(p1.x(), p2.y(), z2)); + + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + + m_planes.models[1].init_from(std::move(init_data)); + } + + const Camera& camera = wxGetApp().plater()->get_camera(); + shader.set_uniform("view_model_matrix", camera.get_view_matrix()); + shader.set_uniform("projection_matrix", camera.get_projection_matrix()); + + m_planes.models[0].set_color((camera_on_top && type == 1) || (!camera_on_top && type == 2) ? SOLID_PLANE_COLOR : TRANSPARENT_PLANE_COLOR); + m_planes.models[0].render(); + m_planes.models[1].set_color((camera_on_top && type == 2) || (!camera_on_top && type == 1) ? SOLID_PLANE_COLOR : TRANSPARENT_PLANE_COLOR); + m_planes.models[1].render(); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index bfee49bd2b..e130cb18be 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -1,7 +1,12 @@ +///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_GUI_Selection_hpp_ #define slic3r_GUI_Selection_hpp_ #include "libslic3r/Geometry.hpp" +#include "GUI_Geometry.hpp" #include "GLModel.hpp" #include @@ -26,58 +31,6 @@ using ModelObjectPtrs = std::vector; namespace GUI { -class TransformationType -{ -public: - enum Enum { - // Transforming in a world coordinate system - World = 0, - // Transforming in a local coordinate system - Local = 1, - // Absolute transformations, allowed in local coordinate system only. - Absolute = 0, - // Relative transformations, allowed in both local and world coordinate system. - Relative = 2, - // For group selection, the transformation is performed as if the group made a single solid body. - Joint = 0, - // For group selection, the transformation is performed on each object independently. - Independent = 4, - - World_Relative_Joint = World | Relative | Joint, - World_Relative_Independent = World | Relative | Independent, - Local_Absolute_Joint = Local | Absolute | Joint, - Local_Absolute_Independent = Local | Absolute | Independent, - Local_Relative_Joint = Local | Relative | Joint, - Local_Relative_Independent = Local | Relative | Independent, - }; - - TransformationType() : m_value(World) {} - TransformationType(Enum value) : m_value(value) {} - TransformationType& operator=(Enum value) { m_value = value; return *this; } - - Enum operator()() const { return m_value; } - bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } - - void set_world() { this->remove(Local); } - void set_local() { this->add(Local); } - void set_absolute() { this->remove(Relative); } - void set_relative() { this->add(Relative); } - void set_joint() { this->remove(Independent); } - void set_independent() { this->add(Independent); } - - bool world() const { return !this->has(Local); } - bool local() const { return this->has(Local); } - bool absolute() const { return !this->has(Relative); } - bool relative() const { return this->has(Relative); } - bool joint() const { return !this->has(Independent); } - bool independent() const { return this->has(Independent); } - -private: - void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); } - void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); } - - Enum m_value; -}; class Selection { @@ -220,9 +173,15 @@ private: GLModel m_arrow; GLModel m_curved_arrow; + GLModel m_box; + struct Planes + { + std::array check_points{ Vec3f::Zero(), Vec3f::Zero() }; + std::array models; + }; + Planes m_planes; float m_scale_factor; - bool m_dragging; // BBS EMode m_volume_selection_mode{ Instance }; @@ -290,6 +249,8 @@ public: bool is_single_volume() const { return m_type == SingleVolume; } bool is_multiple_volume() const { return m_type == MultipleVolume; } bool is_any_volume() const { return is_single_volume() || is_multiple_volume(); } + bool is_any_connector() const; + bool is_any_cut_volume() const; bool is_mixed() const { return m_type == Mixed; } bool is_from_single_instance() const { return get_instance_idx() != -1; } bool is_from_single_object() const; @@ -301,6 +262,8 @@ public: bool contains_all_volumes(const std::vector& volume_idxs) const; // returns true if the selection contains at least one of the given indices bool contains_any_volume(const std::vector& volume_idxs) const; + // returns true if the selection contains any sinking volume + bool contains_sinking_volumes(bool ignore_modifiers = true) const; // returns true if the selection contains all and only the given indices bool matches(const std::vector& volume_idxs) const; @@ -316,6 +279,7 @@ public: const IndicesList& get_volume_idxs() const { return m_list; } const GLVolume* get_volume(unsigned int volume_idx) const; + const GLVolume* get_first_volume() const { return get_volume(*m_list.begin()); } const ObjectIdxsToInstanceIdxsMap& get_content() const { return m_cache.content; } @@ -326,9 +290,7 @@ public: const BoundingBoxf3& get_unscaled_instance_bounding_box() const; const BoundingBoxf3& get_scaled_instance_bounding_box() const; - void start_dragging(); - void stop_dragging() { m_dragging = false; } - bool is_dragging() const { return m_dragging; } + void setup_cache(); void translate(const Vec3d& displacement, bool local = false); void move_to_center(const Vec3d& displacement, bool local = false); @@ -353,16 +315,16 @@ public: void erase(); - void render(float scale_factor = 1.0) const; -#if ENABLE_RENDER_SELECTION_CENTER - void render_center(bool gizmo_is_dragging) const; -#endif // ENABLE_RENDER_SELECTION_CENTER + void render(float scale_factor = 1.0); //BBS: GUI refactor: add uniform scale from gizmo - void render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale) const; + void render_sidebar_hints(const std::string& sidebar_field, bool uniform_scale); +#if ENABLE_RENDER_SELECTION_CENTER + void render_center(bool gizmo_is_dragging); +#endif // ENABLE_RENDER_SELECTION_CENTER bool requires_local_axes() const; - void render_bounding_box(const BoundingBoxf3& box, float* color, float scale) { + void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color, float scale) { m_scale_factor = scale; render_bounding_box(box, color); } @@ -399,14 +361,13 @@ private: void do_remove_instance(unsigned int object_idx, unsigned int instance_idx); void do_remove_object(unsigned int object_idx); void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); } - void render_selected_volumes() const; - void render_synchronized_volumes() const; - void render_bounding_box(const BoundingBoxf3& box, float* color) const; - void render_sidebar_position_hints(const std::string& sidebar_field) const; - void render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void render_synchronized_volumes(); + void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color); + void render_sidebar_position_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix); + void render_sidebar_rotation_hints(const std::string& sidebar_field, GLShaderProgram& shader, const Transform3d& matrix); //BBS: GUI refactor: add uniform_scale from gizmo - void render_sidebar_scale_hints(const std::string& sidebar_field, bool gizmo_uniform_scale) const; - void render_sidebar_layers_hints(const std::string& sidebar_field) const; + void render_sidebar_scale_hints(const std::string& sidebar_field, bool gizmo_uniform_scale, GLShaderProgram& shader, const Transform3d& matrix); + void render_sidebar_layers_hints(const std::string& sidebar_field, GLShaderProgram& shader); public: enum SyncRotationType { diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 5f192f3635..b93c00f767 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -8,6 +8,7 @@ #include "libslic3r/BlacklistedLibraryCheck.hpp" #include "libslic3r/Platform.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Color.hpp" #include "slic3r/GUI/format.hpp" #include "slic3r/Utils/Http.hpp" @@ -591,9 +592,8 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) wxColour bgr_clr = wxGetApp().get_window_default_clr(); SetBackgroundColour(bgr_clr); const auto text_clr = wxGetApp().get_label_clr_default(); - auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); - + auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue())); + auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue())); auto *topSizer = new wxBoxSizer(wxVERTICAL); auto *vsizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index ce7f01f140..c3ff06873d 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -15,6 +15,7 @@ #include "MainFrame.hpp" #include "wxExtensions.hpp" #include "../libslic3r/BlacklistedLibraryCheck.hpp" +#include "../libslic3r/Color.hpp" #include "format.hpp" #ifdef _WIN32 @@ -114,8 +115,8 @@ SysInfoDialog::SysInfoDialog() // main_info_text wxFont font = get_default_font(this); const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue())); + auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue())); const int fs = font.GetPointSize() - 1; int size[] = { static_cast(fs*1.5), static_cast(fs*1.4), static_cast(fs*1.3), fs, fs, fs, fs }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 83827a02d1..93b0437a85 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1911,6 +1911,7 @@ void TabPrint::build() optgroup->append_single_option_line("max_travel_detour_distance"); optgroup->append_single_option_line("extra_perimeters_on_overhangs"); optgroup->append_single_option_line("overhang_reverse"); + optgroup->append_single_option_line("overhang_reverse_internal_only"); optgroup->append_single_option_line("overhang_reverse_threshold"); page = add_options_page(L("Strength"), "empty"); @@ -2574,8 +2575,8 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode) return !invalid; } -static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group, const t_config_option_key& opt_key, const boost::any& value) { - tab->validate_custom_gcodes_was_shown = !Tab::validate_custom_gcode(opt_group->title, boost::any_cast(value)); +static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_config_option_key& opt_key, const boost::any& value) { + tab->validate_custom_gcodes_was_shown = !Tab::validate_custom_gcode(title, boost::any_cast(value)); tab->update_dirty(); tab->on_value_change(opt_key, value); } @@ -2592,18 +2593,19 @@ void TabFilament::add_filament_overrides_page() //BBS line = optgroup->create_single_option_line(optgroup->get_option(opt_key)); - line.near_label_widget = [this, optgroup, opt_key, opt_index](wxWindow* parent) { + line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) { wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, ""); - check_box->Bind(wxEVT_CHECKBOX, [optgroup, opt_key, opt_index](wxCommandEvent& evt) { + check_box->Bind(wxEVT_CHECKBOX, [optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) { const bool is_checked = evt.IsChecked(); - Field* field = optgroup->get_fieldc(opt_key, opt_index); - if (field != nullptr) { - field->toggle(is_checked); - if (is_checked) - field->set_last_meaningful_value(); - else - field->set_na_value(); + if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) { + if (Field *field = optgroup_sh->get_fieldc(opt_key, opt_index); field != nullptr) { + field->toggle(is_checked); + if (is_checked) + field->set_last_meaningful_value(); + else + field->set_na_value(); + } } }, check_box->GetId()); @@ -2760,7 +2762,7 @@ void TabFilament::build() line.append_option(optgroup->get_option("textured_plate_temp")); optgroup->append_line(line); - optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { DynamicPrintConfig& filament_config = wxGetApp().preset_bundle->filaments.get_edited_preset().config; @@ -2855,8 +2857,8 @@ void TabFilament::build() page = add_options_page(L("Advanced"), "advanced"); optgroup = page->new_optgroup(L("Filament start G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("filament_start_gcode"); option.opt.full_width = true; @@ -2865,8 +2867,8 @@ void TabFilament::build() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Filament end G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("filament_end_gcode"); option.opt.full_width = true; @@ -3172,8 +3174,8 @@ void TabPrinter::build_fff() const int notes_field_height = 25; // 250 page = add_options_page(L("Machine gcode"), "cog"); optgroup = page->new_optgroup(L("Machine start G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("machine_start_gcode"); option.opt.full_width = true; @@ -3182,8 +3184,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Machine end G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("machine_end_gcode"); option.opt.full_width = true; @@ -3192,8 +3194,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Before layer change G-code"),"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("before_layer_change_gcode"); option.opt.full_width = true; @@ -3202,8 +3204,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Layer change G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("layer_change_gcode"); option.opt.full_width = true; @@ -3212,8 +3214,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Time lapse G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("time_lapse_gcode"); option.opt.full_width = true; @@ -3222,8 +3224,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Change filament G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("change_filament_gcode"); option.opt.full_width = true; @@ -3232,8 +3234,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key &opt_key, const boost::any &value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("change_extrusion_role_gcode"); @@ -3243,8 +3245,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("machine_pause_gcode"); option.opt.is_code = true; @@ -3252,8 +3254,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Template Custom G-code"), L"param_gcode", 0); - optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, opt_key, value); + optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; option = optgroup->get_option("template_custom_gcode"); option.opt.is_code = true; @@ -3481,7 +3483,7 @@ if (is_marlin_flavor) auto optgroup = page->new_optgroup(L("Single extruder multimaterial setup")); optgroup->append_single_option_line("single_extruder_multi_material", "semm"); // Orca: we only support Single Extruder Multi Material, so it's always enabled - // optgroup->m_on_change = [this, optgroup](const t_config_option_key &opt_key, const boost::any &value) { + // optgroup->m_on_change = [this](const t_config_option_key &opt_key, const boost::any &value) { // wxTheApp->CallAfter([this, opt_key, value]() { // if (opt_key == "single_extruder_multi_material") { // build_unregular_pages(); diff --git a/src/slic3r/GUI/TickCode.cpp b/src/slic3r/GUI/TickCode.cpp index 158afe7a1f..267752c544 100644 --- a/src/slic3r/GUI/TickCode.cpp +++ b/src/slic3r/GUI/TickCode.cpp @@ -4,28 +4,39 @@ namespace Slic3r { namespace GUI { std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder) { + auto opposite_one_color = [](const std::string& color) { + ColorRGB rgb; + decode_color(color, rgb); + return encode_color(opposite(rgb)); + }; + auto opposite_two_colors = [](const std::string& a, const std::string& b) { + ColorRGB rgb1; decode_color(a, rgb1); + ColorRGB rgb2; decode_color(b, rgb2); + return encode_color(opposite(rgb1, rgb2)); + }; + if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) { #if 1 - if (ticks.empty()) return color_generator.get_opposite_color((*m_colors)[0]); + if (ticks.empty()) return opposite_one_color((*m_colors)[0]); auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick); if (before_tick_it == ticks.end()) { while (before_tick_it != ticks.begin()) if (--before_tick_it; before_tick_it->type == ColorChange) break; - if (before_tick_it->type == ColorChange) return color_generator.get_opposite_color(before_tick_it->color); - return color_generator.get_opposite_color((*m_colors)[0]); + if (before_tick_it->type == ColorChange) return opposite_one_color(before_tick_it->color); + return opposite_one_color((*m_colors)[0]); } if (before_tick_it == ticks.begin()) { const std::string &frst_color = (*m_colors)[0]; - if (before_tick_it->type == ColorChange) return color_generator.get_opposite_color(frst_color, before_tick_it->color); + if (before_tick_it->type == ColorChange) return opposite_two_colors(frst_color, before_tick_it->color); auto next_tick_it = before_tick_it; while (next_tick_it != ticks.end()) if (++next_tick_it; next_tick_it->type == ColorChange) break; - if (next_tick_it->type == ColorChange) return color_generator.get_opposite_color(frst_color, next_tick_it->color); + if (next_tick_it->type == ColorChange) return opposite_two_colors(frst_color, next_tick_it->color); - return color_generator.get_opposite_color(frst_color); + return opposite_one_color(frst_color); } std::string frst_color = ""; @@ -44,12 +55,12 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int if (--before_tick_it; before_tick_it->type == ColorChange) break; if (before_tick_it->type == ColorChange) { - if (frst_color.empty()) return color_generator.get_opposite_color(before_tick_it->color); - return color_generator.get_opposite_color(before_tick_it->color, frst_color); + if (frst_color.empty()) return opposite_one_color(before_tick_it->color); + return opposite_two_colors(before_tick_it->color, frst_color); } - if (frst_color.empty()) return color_generator.get_opposite_color((*m_colors)[0]); - return color_generator.get_opposite_color((*m_colors)[0], frst_color); + if (frst_color.empty()) return opposite_one_color((*m_colors)[0]); + return opposite_two_colors((*m_colors)[0], frst_color); #else const std::vector &colors = ColorPrintColors::get(); if (ticks.empty()) return colors[0]; diff --git a/src/slic3r/GUI/TickCode.hpp b/src/slic3r/GUI/TickCode.hpp index 8616d7565a..14785dee55 100644 --- a/src/slic3r/GUI/TickCode.hpp +++ b/src/slic3r/GUI/TickCode.hpp @@ -2,7 +2,7 @@ #define slic3r_GUI_TickCode_hpp_ #include "libslic3r/CustomGCode.hpp" -#include "IMSlider_Utils.hpp" +#include "libslic3r/Color.hpp" #include namespace Slic3r { @@ -29,7 +29,6 @@ class TickCodeInfo bool m_use_default_colors = false; std::vector* m_colors{ nullptr };// reference to IMSlider::m_extruder_colors - ColorGenerator color_generator; std::string get_color_for_tick(TickCode tick, Type type, const int extruder); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index a5868b377b..a04152f004 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -11,6 +11,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Color.hpp" #include "format.hpp" #include "GUI_App.hpp" #include "Plater.hpp" @@ -60,8 +61,7 @@ static std::string get_icon_name(Preset::Type type, PrinterTechnology pt) { static std::string def_text_color() { wxColour def_colour = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), def_colour.Red(), def_colour.Green(), def_colour.Blue()); - return clr_str.ToStdString(); + return encode_color(ColorRGB(def_colour.Red(), def_colour.Green(), def_colour.Blue())); } static std::string grey = "#808080"; static std::string orange = "#ed6b21"; @@ -126,8 +126,8 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) const int icon_height = lround(1.6 * em); BitmapCache bmp_cache; - unsigned char rgb[3]; - BitmapCache::parse_color(into_u8(color), rgb); + ColorRGB rgb; + decode_color(into_u8(color), rgb); // there is no need to scale created solid bitmap #ifndef __linux__ return bmp_cache.mksolid(icon_width, icon_height, rgb, true); diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 81e7b28abf..f2d226bc23 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -7,6 +7,7 @@ #include "I18N.hpp" #include "GUI_App.hpp" #include "MsgDialog.hpp" +#include "libslic3r/Color.hpp" #include "Widgets/Button.hpp" #include "slic3r/Utils/ColorSpaceConvert.hpp" #include "MainFrame.hpp" @@ -408,9 +409,9 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); for (const std::string& color : extruder_colours) { - //unsigned char rgb[3]; - //Slic3r::GUI::BitmapCache::parse_color(color, rgb); - m_colours.push_back(wxColor(color)); + Slic3r::ColorRGB rgb; + Slic3r::decode_color(color, rgb); + m_colours.push_back(wxColor(rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar())); } m_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 6561bedfe7..8186f4dfc2 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -4,6 +4,7 @@ #include "../GUI/DeviceManager.hpp" #include "../GUI/Jobs/ProgressIndicator.hpp" #include "../GUI/PartPlate.hpp" +#include "libslic3r/CutUtils.hpp" #include "libslic3r/Model.hpp" @@ -133,7 +134,7 @@ bool CalibUtils::validate_input_flow_ratio(wxString flow_ratio, float* output_va return true; } -static void cut_model(Model &model, std::array plane_points, ModelObjectCutAttributes attributes) +static void cut_model(Model &model, double z, ModelObjectCutAttributes attributes) { size_t obj_idx = 0; size_t instance_idx = 0; @@ -142,7 +143,9 @@ static void cut_model(Model &model, std::array plane_points, ModelObje auto* object = model.objects[0]; - const auto new_objects = object->cut(instance_idx, plane_points, attributes); + const Vec3d instance_offset = object->instances[instance_idx]->get_offset(); + Cut cut(object, instance_idx, Geometry::translation_transform(z * Vec3d::UnitZ() - instance_offset), attributes); + const auto new_objects = cut.perform_with_plane(); model.delete_object(obj_idx); for (ModelObject *model_object : new_objects) { @@ -173,16 +176,6 @@ static void read_model_from_file(const std::string& input_file, Model& model) object->ensure_on_bed(); } -std::array get_cut_plane_points(const BoundingBoxf3 &bbox, const double &cut_height) -{ - std::array plane_pts; - plane_pts[0] = Vec3d(bbox.min(0), bbox.min(1), cut_height); - plane_pts[1] = Vec3d(bbox.max(0), bbox.min(1), cut_height); - plane_pts[2] = Vec3d(bbox.max(0), bbox.max(1), cut_height); - plane_pts[3] = Vec3d(bbox.min(0), bbox.max(1), cut_height); - return plane_pts; -} - void CalibUtils::calib_PA(const X1CCalibInfos& calib_infos, int mode, wxString& error_message) { DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); @@ -564,12 +557,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess // add EPSILON offset to avoid cutting at the exact location where the flat surface is auto new_height = block_count * 10.0 + EPSILON; if (new_height < obj_bb.size().z()) { - std::array plane_pts; - plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), new_height); - plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), new_height); - plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), new_height); - plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), new_height); - cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_model(model, new_height, ModelObjectCutAttribute::KeepLower); } } @@ -579,12 +567,7 @@ void CalibUtils::calib_temptue(const CalibInfo &calib_info, wxString &error_mess if (block_count > 0) { auto new_height = block_count * 10.0 + EPSILON; if (new_height < obj_bb.size().z()) { - std::array plane_pts; - plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), new_height); - plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), new_height); - plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), new_height); - plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), new_height); - cut_model(model, plane_pts, ModelObjectCutAttribute::KeepUpper); + cut_model(model, new_height, ModelObjectCutAttribute::KeepUpper); } } @@ -670,12 +653,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro auto obj_bb = obj->bounding_box(); double height = (params.end - params.start + 1) / params.step; if (height < obj_bb.size().z()) { - std::array plane_pts; - plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), height); - plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), height); - plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), height); - plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), height); - cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_model(model, height, ModelObjectCutAttribute::KeepLower); } auto new_params = params; @@ -731,12 +709,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message) auto obj_bb = model.objects[0]->bounding_box(); auto height = 5 * ((params.end - params.start) / params.step + 1); if (height < obj_bb.size().z()) { - std::array plane_pts; - plane_pts[0] = Vec3d(obj_bb.min(0), obj_bb.min(1), height); - plane_pts[1] = Vec3d(obj_bb.max(0), obj_bb.min(1), height); - plane_pts[2] = Vec3d(obj_bb.max(0), obj_bb.max(1), height); - plane_pts[3] = Vec3d(obj_bb.min(0), obj_bb.max(1), height); - cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_model(model, height, ModelObjectCutAttribute::KeepLower); } else { error_message = _L("The start, end or step is not valid value."); @@ -790,8 +763,7 @@ void CalibUtils::calib_retraction(const CalibInfo &calib_info, wxString &error_m auto obj_bb = obj->bounding_box(); auto height = 1.0 + 0.4 + ((params.end - params.start)) / params.step; if (height < obj_bb.size().z()) { - std::array plane_pts = get_cut_plane_points(obj_bb, height); - cut_model(model, plane_pts, ModelObjectCutAttribute::KeepLower); + cut_model(model, height, ModelObjectCutAttribute::KeepLower); } DynamicPrintConfig full_config; @@ -909,9 +881,9 @@ void CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f //draw thumbnails { GLVolumeCollection glvolume_collection; - std::vector> colors_out(1); + std::vector colors_out(1); unsigned char rgb_color[4] = {255, 255, 255, 255}; - std::array new_color {1.0f, 1.0f, 1.0f, 1.0f}; + ColorRGBA new_color {1.0f, 1.0f, 1.0f, 1.0f}; colors_out.push_back(new_color); ThumbnailData* thumbnail_data = &plate_data_list[0]->plate_thumbnail; @@ -926,8 +898,8 @@ void CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f const ModelVolume &model_volume = *model_object.volumes[volume_idx]; for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { const ModelInstance &model_instance = *model_object.instances[instance_idx]; - glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true); - glvolume_collection.volumes.back()->set_render_color( new_color[0], new_color[1], new_color[2], new_color[3]); + glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, false, true); + glvolume_collection.volumes.back()->set_render_color(new_color); glvolume_collection.volumes.back()->set_color(new_color); //glvolume_collection.volumes.back()->printable = model_instance.printable; } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 56a5172ab3..eaf90c7349 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -21,6 +21,7 @@ #include #include +#include "slic3r/GUI/3DScene.hpp" #include #ifndef NDEBUG diff --git a/version.inc b/version.inc index bfac89afd2..7e062b19c9 100644 --- a/version.inc +++ b/version.inc @@ -10,7 +10,7 @@ endif() if(NOT DEFINED BBL_INTERNAL_TESTING) set(BBL_INTERNAL_TESTING "1") endif() -set(SoftFever_VERSION "1.8.0-rc2") +set(SoftFever_VERSION "1.8.0") string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" SoftFever_VERSION_MATCH ${SoftFever_VERSION}) set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})