Merge branch 'main' into draco

This commit is contained in:
Maeyanie
2026-01-24 23:05:49 -05:00
committed by GitHub
3292 changed files with 529107 additions and 112278 deletions

View File

@@ -0,0 +1,19 @@
---
allowed-tools: Bash(git checkout --branch:*), Bash(git add:*), Bash(git status:*), Bash(git push:*), Bash(git commit:*), Bash(gh pr create:*)
description: Commit, push, and open a PR
---
## Context
- Current git status: !`git status`
- Current git diff (staged and unstaged changes): !`git diff HEAD`
- Current branch: !`git branch --show-current`
## Your task
Based on the above changes:
1. Create a new branch if on main
2. Create a single commit with an appropriate message
3. Push the branch to origin
4. Create a pull request using `gh pr create`
5. You have the capability to call multiple tools in a single response. You MUST do all of the above in a single message. Do not use any other tools or do anything else. Do not send any other text or messages besides these tool calls.

View File

@@ -0,0 +1,38 @@
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
description: Find duplicate GitHub issues
---
Find up to 3 likely duplicate issues for a given GitHub issue.
To do this, follow these steps precisely:
1. Use an agent to check if the Github issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed.
2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue
3. Then, launch 5 parallel agents to search Github for duplicates of this issue, using diverse keywords and search approaches, using the summary from #1
4. Next, feed the results from #1 and #2 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
Notes (be sure to tell this to your agents, too):
- Use `gh` to interact with Github, rather than web fetch
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
- Make a todo list first
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
---
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 OrcaSlicer bot
---

View File

@@ -0,0 +1,40 @@
---
allowed-tools: Bash(gh issue list:*), Bash(gh issue view:*), Bash(gh issue edit:*), TodoWrite
description: Triage GitHub issues and label critical ones for oncall
---
You're an oncall triage assistant for GitHub issues. Your task is to identify critical issues that require immediate oncall attention and apply the "oncall" label.
Repository: OrcaSlicer/OrcaSlicer
Task overview:
1. First, get all open bugs updated in the last 3 days with at least 50 engagements:
```bash
gh issue list --repo OrcaSlicer/OrcaSlicer --state open --label bug --limit 1000 --json number,title,updatedAt,comments,reactions | jq -r '.[] | select((.updatedAt >= (now - 259200 | strftime("%Y-%m-%dT%H:%M:%SZ"))) and ((.comments | length) + ([.reactions[].content] | length) >= 50)) | "\(.number)"'
```
2. Save the list of issue numbers and create a TODO list with ALL of them. This ensures you process every single one.
3. For each issue in your TODO list:
- Use `gh issue view <number> --repo OrcaSlicer/OrcaSlicer --json title,body,labels,comments` to get full details
- Read and understand the full issue content and comments to determine actual user impact
- Evaluate: Is this truly blocking users from using Claude Code?
- Consider: "crash", "stuck", "frozen", "hang", "unresponsive", "cannot use", "blocked", "broken"
- Does it prevent core functionality? Can users work around it?
- Be conservative - only flag issues that truly prevent users from getting work done
4. For issues that are truly blocking and don't already have the "oncall" label:
- Use `gh issue edit <number> --repo OrcaSlicer/OrcaSlicer --add-label "oncall"`
- Mark the issue as complete in your TODO list
5. After processing all issues, provide a summary:
- List each issue number that received the "oncall" label
- Include the issue title and brief reason why it qualified
- If no issues qualified, state that clearly
Important:
- Process ALL issues in your TODO list systematically
- Don't post any comments to issues
- Only add the "oncall" label, never remove it
- Use individual `gh issue view` commands instead of bash for loops to avoid approval prompts

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = OrcaSlicer
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.6.3
PROJECT_NUMBER = latest
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -68,7 +68,7 @@ PROJECT_LOGO = ./resources/images/OrcaSlicer_32px.png
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = ../
OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
@@ -1059,7 +1059,11 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
EXCLUDE_PATTERNS = */deps/*
EXCLUDE_PATTERNS = */build/*
EXCLUDE_PATTERNS = */deps_src/*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1286,7 +1290,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = OrcaSlicer_Dev_Document
HTML_OUTPUT = internal_docs
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
@@ -1565,7 +1569,7 @@ TOC_EXPAND = NO
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
SITEMAP_URL = internals.orcaslicer.com
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that

View File

@@ -8,7 +8,7 @@ body:
**Thank you for using Orca Slicer and wanting to report a bug.**
Please note that this is not the place to make feature requests or ask for help.
For this, please use the [Feature request](https://github.com/SoftFever/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=feature_request.yml) issue type or you can discuss your idea on our [Discord server](https://discord.gg/P4VE9UY9gJ) with others.
For this, please use the [Feature request](https://github.com/OrcaSlicer/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=feature_request.yml) issue type or you can discuss your idea on our [Discord server](https://discord.gg/P4VE9UY9gJ) with others.
Before filing, please check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
- type: checkboxes

View File

@@ -4,5 +4,5 @@ contact_links:
url: https://discord.gg/P4VE9UY9gJ
about: Please ask and answer support "how do I?"questions here.
- name: Discussion Forum
url: https://github.com/SoftFever/OrcaSlicer/discussions
url: https://github.com/OrcaSlicer/OrcaSlicer/discussions
about: Please raise ideas and feature suggestions here.

View File

@@ -8,7 +8,7 @@ body:
Thanks for taking the time to fill out this feature request!
If your idea is still at the formulation stage, or you're not sure it would
be useful to many users, you can raise it as a discussion topic under [Ideas](https://github.com/SoftFever/OrcaSlicer/discussions/categories/ideas)
be useful to many users, you can raise it as a discussion topic under [Ideas](https://github.com/OrcaSlicer/OrcaSlicer/discussions/categories/ideas)
or you can raise it on the [Discord server](https://discord.gg/P4VE9UY9gJ).
- type: checkboxes
attributes:

View File

@@ -14,7 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Assign the user or unassign stale assignments
uses: takanome-dev/assign-issue-action@v2.3
# Note: v3.0.0 is broken (dist/index.mjs vs action.yml expects index.js)
# See: https://github.com/takanome-dev/assign-issue-action/issues/426
uses: takanome-dev/assign-issue-action@v2.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
maintainers: 'noisyfox,softfever'

View File

@@ -0,0 +1,31 @@
name: Auto-close duplicate issues
description: Auto-closes issues that are duplicates of existing issues
on:
schedule:
- cron: "0 9 * * *"
workflow_dispatch:
jobs:
auto-close-duplicates:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Auto-close duplicate issues
run: bun run scripts/auto-close-duplicates.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}

View File

@@ -0,0 +1,44 @@
name: Backfill Duplicate Comments
description: Triggers duplicate detection for old issues that don't have duplicate comments
on:
workflow_dispatch:
inputs:
days_back:
description: 'How many days back to look for old issues'
required: false
default: '90'
type: string
dry_run:
description: 'Dry run mode (true to only log what would be done)'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'
jobs:
backfill-duplicate-comments:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
issues: read
actions: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Backfill duplicate comments
run: bun run scripts/backfill-duplicate-comments.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DAYS_BACK: ${{ inputs.days_back }}
DRY_RUN: ${{ inputs.dry_run }}

View File

@@ -21,6 +21,7 @@ on:
- release/*
paths:
- 'deps/**'
- 'deps_src/**'
- 'src/**'
- '**/CMakeLists.txt'
- 'version.inc'
@@ -30,6 +31,7 @@ on:
- 'build_release_macos.sh'
- 'scripts/flatpak/**'
schedule:
- cron: '0 17 * * *' # run once a day at 1 AM Singapore time (UTC+8)
@@ -51,7 +53,7 @@ jobs:
strategy:
fail-fast: false
# Don't run scheduled builds on forks:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'SoftFever/OrcaSlicer') }}
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
uses: ./.github/workflows/build_check_cache.yml
with:
os: ubuntu-24.04
@@ -64,10 +66,10 @@ jobs:
matrix:
include:
- os: windows-latest
- os: macos-14
- os: orca-macos-arm64
arch: arm64
# Don't run scheduled builds on forks:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'SoftFever/OrcaSlicer') }}
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
uses: ./.github/workflows/build_check_cache.yml
with:
os: ${{ matrix.os }}
@@ -82,15 +84,16 @@ jobs:
if: ${{ !cancelled() && success() }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
sparse-checkout: |
.github
scripts
tests
- name: Apt-Install Dependencies
uses: ./.github/actions/apt-install-deps
- name: Restore Test Artifact
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: ${{ github.sha }}-tests
- uses: lukka/get-cmake@latest
@@ -102,7 +105,7 @@ jobs:
tar -xvf build_tests.tar
scripts/run_unit_tests.sh
- name: Upload Test Logs
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
if: ${{ failure() }}
with:
name: unit-test-logs
@@ -115,13 +118,14 @@ jobs:
flatpak:
name: "Flatpak"
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-47
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
options: --privileged
volumes:
- /usr/local/lib/android:/usr/local/lib/android
- /usr/share/dotnet:/usr/share/dotnet
- /opt/ghc:/opt/ghc1
- /usr/local/share/boost:/usr/local/share/boost1
- /opt/hostedtoolcache:/opt/hostedtoolcache1
strategy:
fail-fast: false
matrix:
@@ -131,7 +135,7 @@ jobs:
- arch: aarch64
runner: ubuntu-24.04-arm
# Don't run scheduled builds on forks:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'SoftFever/OrcaSlicer') }}
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
runs-on: ${{ matrix.variant.runner }}
env:
date:
@@ -140,8 +144,8 @@ jobs:
steps:
- name: "Remove unneeded stuff to free disk space"
run:
rm -rf /usr/local/lib/android/* /usr/share/dotnet/* /opt/ghc1/* "/usr/local/share/boost1/*"
- uses: actions/checkout@v5
rm -rf /usr/local/lib/android/* /usr/share/dotnet/* /opt/ghc1/* "/usr/local/share/boost1/*" /opt/hostedtoolcache1/*
- uses: actions/checkout@v6
- name: Get the version and date
run: |
ver_pure=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
@@ -157,7 +161,7 @@ jobs:
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
shell: bash
- uses: flathub-infra/flatpak-github-actions/flatpak-builder@master
- uses: flatpak/flatpak-github-actions/flatpak-builder@master
with:
bundle: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
manifest-path: scripts/flatpak/io.github.softfever.OrcaSlicer.yml
@@ -165,15 +169,15 @@ jobs:
arch: ${{ matrix.variant.arch }}
upload-artifact: false
- name: Upload artifacts Flatpak
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
path: '/__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak'
- name: Deploy Flatpak to nightly release
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: /__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak

View File

@@ -26,14 +26,14 @@ jobs:
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
lfs: 'true'
- name: set outputs
id: set_outputs
env:
dep-folder-name: ${{ inputs.os != 'macos-14' && '/OrcaSlicer_dep' || '' }}
dep-folder-name: ${{ inputs.os != 'orca-macos-arm64' && '/OrcaSlicer_dep' || '' }}
output-cmd: ${{ inputs.os == 'windows-latest' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
run: |
echo cache-key=${{ inputs.os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}
@@ -41,7 +41,7 @@ jobs:
- name: load cache
id: cache_deps
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ steps.set_outputs.outputs.cache-path }}
key: ${{ steps.set_outputs.outputs.cache-key }}

View File

@@ -34,12 +34,12 @@ jobs:
# Setup the environment
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
lfs: 'true'
- name: load cached deps
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ inputs.cache-path }}
key: ${{ inputs.cache-key }}
@@ -69,23 +69,23 @@ jobs:
working-directory: ${{ github.workspace }}
run: |
choco install strawberryperl
.\build_release_vs2022.bat deps
.\build_release_vs2022.bat pack
.\build_release_vs.bat deps
.\build_release_vs.bat pack
cd ${{ github.workspace }}/deps/build
- name: Build on Mac ${{ inputs.arch }}
if: inputs.os == 'macos-14'
if: inputs.os == 'orca-macos-arm64'
working-directory: ${{ github.workspace }}
run: |
brew install automake texinfo libtool
brew list
brew uninstall --ignore-dependencies zstd
./build_release_macos.sh -dx -a universal -t 10.15 -1
# brew install automake texinfo libtool
# brew list
# brew uninstall --ignore-dependencies zstd
./build_release_macos.sh -dx -a universal -t 10.15
for arch in arm64 x86_64; do
(cd "${{ github.workspace }}/deps/build/${arch}" && \
find . -mindepth 1 -maxdepth 1 ! -name 'OrcaSlicer_dep' -exec rm -rf {} +)
done
brew install zstd
# brew install zstd
- name: Apt-Install Dependencies
@@ -104,15 +104,15 @@ jobs:
# Upload Artifacts
# - name: Upload Mac ${{ inputs.arch }} artifacts
# if: inputs.os == 'macos-14'
# uses: actions/upload-artifact@v5
# if: inputs.os == 'orca-macos-arm64'
# uses: actions/upload-artifact@v6
# with:
# name: OrcaSlicer_dep_mac_${{ env.date }}
# path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep*.tar.gz
- name: Upload Windows artifacts
if: inputs.os == 'windows-latest'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_dep_win64_${{ env.date }}
path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep*.zip
@@ -121,7 +121,7 @@ jobs:
if: ${{ ! env.ACT && inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04' }}
env:
ubuntu-ver: ${{ (inputs.os == 'ubuntu-20.04' && '2004') || (inputs.os == 'ubuntu-24.04' && '2404') || '' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_dep_ubuntu_${{ env.ubuntu-ver }}_${{ env.date }}
path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep_ubuntu_*.tar.gz

View File

@@ -26,12 +26,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
lfs: 'true'
- name: load cached deps
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ inputs.cache-path }}
key: ${{ inputs.cache-key }}
@@ -86,29 +86,29 @@ jobs:
# Mac
- name: Install tools mac
if: inputs.os == 'macos-14'
if: inputs.os == 'orca-macos-arm64'
run: |
brew install libtool
brew list
# brew install libtool
# brew list
mkdir -p ${{ github.workspace }}/deps/build
- name: Free disk space
if: inputs.os == 'macos-14'
run: |
df -hI /dev/disk3s1s1
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} +
sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
df -hI /dev/disk3s1s1
# - name: Free disk space
# if: inputs.os == 'orca-macos-arm64'
# run: |
# df -hI /dev/disk3s1s1
# sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} +
# sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
# df -hI /dev/disk3s1s1
- name: Build slicer mac
if: inputs.os == 'macos-14'
if: inputs.os == 'orca-macos-arm64'
working-directory: ${{ github.workspace }}
run: |
./build_release_macos.sh -s -n -x -a universal -t 10.15 -1
./build_release_macos.sh -s -n -x -a universal -t 10.15
# Thanks to RaySajuuk, it's working now
- name: Sign app and notary
if: github.repository == 'SoftFever/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && inputs.os == 'macos-14'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && inputs.os == 'orca-macos-arm64'
working-directory: ${{ github.workspace }}
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
@@ -122,6 +122,8 @@ jobs:
security create-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
# Set the temporary keychain as the default to prevent codesign from accessing the locked login keychain
security default-keychain -s "$KEYCHAIN_PATH"
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
@@ -150,18 +152,17 @@ jobs:
fi
# Notarize main 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_universal_${{ env.ver }}.dmg" --keychain-profile "notarytool-profile" --wait
xcrun notarytool submit "OrcaSlicer_Mac_universal_${{ env.ver }}.dmg" --apple-id "${{ secrets.APPLE_DEV_ACCOUNT }}" --team-id "${{ secrets.TEAM_ID }}" --password "${{ secrets.APP_PWD }}" --wait
xcrun stapler staple OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Notarize profile validator DMG if it exists
if [ -f "OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg" ]; then
xcrun notarytool submit "OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg" --keychain-profile "notarytool-profile" --wait
xcrun notarytool submit "OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg" --apple-id "${{ secrets.APPLE_DEV_ACCOUNT }}" --team-id "${{ secrets.TEAM_ID }}" --password "${{ secrets.APP_PWD }}" --wait
xcrun stapler staple OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
- name: Create DMG without notary
if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14'
if: github.ref != 'refs/heads/main' && inputs.os == 'orca-macos-arm64'
working-directory: ${{ github.workspace }}
run: |
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
@@ -180,25 +181,25 @@ jobs:
fi
- name: Upload artifacts mac
if: inputs.os == 'macos-14'
uses: actions/upload-artifact@v5
if: inputs.os == 'orca-macos-arm64'
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_Mac_universal_${{ env.ver }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
- name: Upload OrcaSlicer_profile_validator DMG mac
if: inputs.os == 'macos-14'
uses: actions/upload-artifact@v5
if: inputs.os == 'orca-macos-arm64'
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_profile_validator_Mac_universal_DMG_${{ env.ver }}
path: ${{ github.workspace }}/OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
if-no-files-found: ignore
- name: Deploy Mac release
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'macos-14'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'orca-macos-arm64'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
asset_name: OrcaSlicer_Mac_universal_nightly.dmg
@@ -206,10 +207,10 @@ jobs:
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Deploy Mac OrcaSlicer_profile_validator DMG release
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'macos-14'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'orca-macos-arm64'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
asset_name: OrcaSlicer_profile_validator_Mac_universal_nightly.dmg
@@ -233,7 +234,7 @@ jobs:
env:
WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
WindowsSDKVersion: '10.0.26100.0\'
run: .\build_release_vs2022.bat slicer
run: .\build_release_vs.bat slicer
- name: Create installer Win
if: inputs.os == 'windows-latest'
@@ -255,37 +256,37 @@ jobs:
- name: Upload artifacts Win zip
if: inputs.os == 'windows-latest'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_Windows_${{ env.ver }}_portable
path: ${{ github.workspace }}/build/OrcaSlicer
- name: Upload artifacts Win installer
if: inputs.os == 'windows-latest'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_Windows_${{ env.ver }}
path: ${{ github.workspace }}/build/OrcaSlicer*.exe
- name: Upload artifacts Win PDB
if: inputs.os == 'windows-latest'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: PDB
path: ${{ github.workspace }}/build/src/Release/Debug_PDB_${{ env.ver }}_for_developers_only.7z
- name: Upload OrcaSlicer_profile_validator Win
if: inputs.os == 'windows-latest'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_profile_validator_Windows_${{ env.ver }}
path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
- name: Deploy Windows release portable
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/build/OrcaSlicer_Windows_${{ env.ver }}_portable.zip
asset_name: OrcaSlicer_Windows_nightly_portable.zip
@@ -293,10 +294,10 @@ jobs:
max_releases: 1
- name: Deploy Windows release installer
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/build/OrcaSlicer_Windows_Installer_${{ env.ver }}.exe
asset_name: OrcaSlicer_Windows_Installer_nightly.exe
@@ -304,10 +305,10 @@ jobs:
max_releases: 1
- name: Deploy Windows OrcaSlicer_profile_validator release
if: github.repository == 'SoftFever/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
asset_name: OrcaSlicer_profile_validator_Windows_nightly.exe
@@ -336,7 +337,7 @@ jobs:
# and doesn't preserve file permissions
- name: Upload Test Artifact
if: inputs.os == 'ubuntu-24.04'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: ${{ github.sha }}-tests
overwrite: true
@@ -358,7 +359,7 @@ jobs:
env:
ubuntu-ver: ${{ (inputs.os == 'ubuntu-20.04' && '2004') || (inputs.os == 'ubuntu-24.04' && '2404') || '' }}
ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
@@ -367,25 +368,25 @@ jobs:
if: ${{ ! env.ACT && inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04' }}
env:
ubuntu-ver: ${{ (inputs.os == 'ubuntu-20.04' && '2004') || (inputs.os == 'ubuntu-24.04' && '2404') || '' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
path: './build/src/Release/OrcaSlicer_profile_validator'
- name: Deploy Ubuntu release
if: ${{ github.repository == 'SoftFever/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
env:
ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.AppImage
asset_content_type: application/octet-stream
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Deploy Ubuntu release
if: ${{ github.repository == 'SoftFever/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && inputs.os == 'ubuntu-24.04' }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && inputs.os == 'ubuntu-24.04' }}
uses: rickstaa/action-create-tag@v1
with:
tag: "nightly-builds"
@@ -394,12 +395,12 @@ jobs:
message: "nightly-builds"
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
if: ${{ github.repository == 'SoftFever/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
env:
ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ./build/src/Release/OrcaSlicer_profile_validator
asset_name: OrcaSlicer_profile_validator_Linux${{ env.ubuntu-ver-str }}_nightly
@@ -407,10 +408,10 @@ jobs:
max_releases: 1
- name: Deploy orca_custom_preset_tests
if: ${{ github.repository == 'SoftFever/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && inputs.os == 'ubuntu-24.04' }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && inputs.os == 'ubuntu-24.04' }}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/SoftFever/OrcaSlicer/releases/137995723/assets{?name,label}
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ${{ github.workspace }}/resources/profiles/orca_custom_preset_tests.zip
asset_name: orca_custom_preset_tests.zip

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install gettext
run: |

View File

@@ -1,8 +1,8 @@
name: Check profiles
on:
on:
pull_request:
branches:
- main
branches:
- main
paths:
- 'resources/profiles/**'
- ".github/workflows/check_profiles.yml"
@@ -10,25 +10,33 @@ on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
description: 'Log level'
required: true
default: 'warning'
permissions:
pull-requests: write
contents: read
jobs:
check_translation:
name: Check profiles
runs-on: ubuntu-24.04
steps:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Run extra JSON check
id: extra_json_check
continue-on-error: true
run: |
python3 ./scripts/orca_extra_profile_check.py
set +e
python3 ./scripts/orca_extra_profile_check.py 2>&1 | tee ${{ runner.temp }}/extra_json_check.log
exit ${PIPESTATUS[0]}
# download
- name: Download
- name: Download
working-directory: ${{ github.workspace }}
run: |
curl -LJO https://github.com/SoftFever/Orca_tools/releases/download/1/OrcaSlicer_profile_validator
@@ -36,15 +44,71 @@ jobs:
# validate profiles
- name: validate system profiles
id: validate_system
continue-on-error: true
run: |
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2
set +e
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_system.log
exit ${PIPESTATUS[0]}
- name: validate custom presets
id: validate_custom
continue-on-error: true
working-directory: ${{ github.workspace }}
run: |
curl -LJO https://github.com/SoftFever/OrcaSlicer/releases/download/nightly-builds/orca_custom_preset_tests.zip
unzip ./orca_custom_preset_tests.zip -d ${{ github.workspace }}/resources/profiles
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2
set +e
curl -LJO https://github.com/OrcaSlicer/OrcaSlicer/releases/download/nightly-builds/orca_custom_preset_tests.zip
unzip -q ./orca_custom_preset_tests.zip -d ${{ github.workspace }}/resources/profiles
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_custom.log
exit ${PIPESTATUS[0]}
- name: Post error comment on PR
if: ${{ always() && github.event_name == 'pull_request' && (steps.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
env:
GH_TOKEN: ${{ github.token }}
run: |
{
echo "## :x: Profile Validation Errors"
echo ""
if [ "${{ steps.extra_json_check.outcome }}" = "failure" ]; then
echo "### Extra JSON Check Failed"
echo ""
echo '```'
head -c 30000 ${{ runner.temp }}/extra_json_check.log || echo "No output captured"
echo '```'
echo ""
fi
if [ "${{ steps.validate_system.outcome }}" = "failure" ]; then
echo "### System Profile Validation Failed"
echo ""
echo '```'
head -c 30000 ${{ runner.temp }}/validate_system.log || echo "No output captured"
echo '```'
echo ""
fi
if [ "${{ steps.validate_custom.outcome }}" = "failure" ]; then
echo "### Custom Preset Validation Failed"
echo ""
echo '```'
head -c 30000 ${{ runner.temp }}/validate_custom.log || echo "No output captured"
echo '```'
echo ""
fi
echo "---"
echo "*Please fix the above errors and push a new commit.*"
} > ${{ runner.temp }}/pr_comment.md
gh pr comment ${{ github.event.pull_request.number }} --body-file ${{ runner.temp }}/pr_comment.md
- name: Fail if any check failed
if: ${{ always() && (steps.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
run: |
echo "One or more profile checks failed. See above for details."
exit 1

81
.github/workflows/dedupe-issues.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Orca Issue Dedupe
description: Automatically dedupe GitHub issues using AI
on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to process for duplicate detection'
required: true
type: string
jobs:
dedupe-issues:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Run Claude Code slash command
uses: anthropics/claude-code-base-action@beta
with:
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: "--model claude-sonnet-4-5-20250929"
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log duplicate comment event to Statsig
if: always()
env:
STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
run: |
ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
REPO=${{ github.repository }}
if [ -z "$STATSIG_API_KEY" ]; then
echo "STATSIG_API_KEY not found, skipping Statsig logging"
exit 0
fi
# Prepare the event payload
EVENT_PAYLOAD=$(jq -n \
--arg issue_number "$ISSUE_NUMBER" \
--arg repo "$REPO" \
--arg triggered_by "${{ github.event_name }}" \
'{
events: [{
eventName: "github_duplicate_comment_added",
value: 1,
metadata: {
repository: $repo,
issue_number: ($issue_number | tonumber),
triggered_by: $triggered_by,
workflow_run_id: "${{ github.run_id }}"
},
time: (now | floor | tostring)
}]
}')
# Send to Statsig API
echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}"
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
-H "Content-Type: application/json" \
-H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
-d "$EVENT_PAYLOAD")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | head -n-1)
if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}"
else
echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
fi

78
.github/workflows/doxygen-docs.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: Generate Doxygen Documentation
on:
schedule:
- cron: '0 0 * * 1' # Every Monday at midnight UTC
workflow_dispatch: # Manual trigger
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
build-and-deploy:
name: Build and Deploy Docs
runs-on: ubuntu-latest
timeout-minutes: 60
# Only run on main branch of the main repository
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main'
permissions:
contents: read
steps:
- uses: thejerrybao/setup-swap-space@v1
with:
swap-space-path: /swapfile
swap-size-gb: 8
remove-existing-swap-files: true
- name: Checkout repository
uses: actions/checkout@v6
- name: Install Doxygen and Graphviz
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y doxygen graphviz
- name: Generate documentation
run: |
set -euo pipefail
# Override DOT_NUM_THREADS to avoid parallel dot race condition bug
sed -i 's/^DOT_NUM_THREADS.*/DOT_NUM_THREADS = 1/' .doxygen
doxygen .doxygen
# Verify documentation was generated
if [ ! -f "internal_docs/index.html" ]; then
echo "Error: Documentation generation failed - index.html not found"
exit 1
fi
- name: Install Rclone
run: |
set -euo pipefail
sudo -v
curl -fsSL https://rclone.org/install.sh | sudo bash
- name: optimize
run: |
set -euo pipefail
rm -f internal_docs/Nodes.xml internal_docs/Tokens.xml
find internal_docs -name "*.map" -type f -delete || true
find internal_docs -name "*.md5" -type f -delete || true
- name: upload
# We configure rclone dynamically using environment variables
run: |
set -euo pipefail
# Remove existing config if it exists to avoid conflicts
rclone config delete cloudflare 2>/dev/null || true
rclone config create cloudflare s3 \
provider Cloudflare \
access_key_id ${{ secrets.R2_ACCESS_KEY_ID }} \
secret_access_key ${{ secrets.R2_SECRET_ACCESS_KEY }} \
endpoint ${{ secrets.R2_ENDPOINT }}
rclone sync internal_docs/ cloudflare:orcaslicer-internals \
--progress \
--transfers 512 \
--checkers 512
echo "Documentation upload completed successfully"

View File

@@ -1,73 +0,0 @@
name: Orca bot
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: write # only for delete-branch option
steps:
- uses: actions/stale@v10
with:
# PAT for GitHub API authentication
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Max number of operations per run
operations-per-run: 1000
# Order to get issues/PRs
ascending: true
# ISSUES
# Do not auto-close an issue if it is assigned to a milestone
exempt-all-issue-milestones: true
# Exempt all issues with assignees from stale
exempt-all-issue-assignees: true
# Exempt feature requests
exempt-issue-labels: "enhancement"
# Idle number of days before marking issues stale
days-before-issue-stale: 90
# Idle number of days before marking issues close
days-before-issue-close: 7
# Label to apply on staled issues
stale-issue-label: "stale"
# Issue close reason
close-issue-reason: not_planned
# Remove stale label from issues on updates
remove-issue-stale-when-updated: true
# Issue stale message
stale-issue-message: "Orca bot: this issue is stale because it has been open for 90 days with no activity."
# Issue closure message
close-issue-message: "Orca bot: This issue was closed because it has been inactive for 7 days since being marked as stale."
# PRs
# Do not auto-close a PR if it is assigned to a milestone
exempt-all-pr-milestones: true
# Exempt all PRs with assignees from stale
exempt-all-pr-assignees: true
# Skip the stale action for draft PRs
exempt-draft-pr: true
# Idle number of days before marking PRs stale
days-before-pr-stale: -1
# Idle number of days before marking PRs close
days-before-pr-close: -1
# Label to apply on staled PRs
stale-pr-label: "stale"
# Label to apply on closed PRs
close-pr-label: not_planned
# Remove stale label from PRs on updates
remove-pr-stale-when-updated: true
# PR stale message
stale-pr-message: "Orca bot: this PR is stale because it has been open for XX days with no activity."
# PR closure message
close-pr-message: "Orca bot: This PR was closed because it has been inactive for X days since being marked as stale."
# Delete branch after closing a stale PR
delete-branch: true

View File

@@ -1,46 +0,0 @@
name: Publish docs to Wiki
# Trigger this action only if there are changes pushed to the doc/** directory under the main branch
on:
push:
paths:
- doc/** # This includes all sub folders
branches:
- main # This can be changed to any branch of your preference
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
env:
USER_TOKEN: ${{ secrets.GH_WIKI_PAT }} # This is the repository secret personal access token
USER_NAME: ${{ vars.BOT_USER_NAME }} # Enter the username of your (bot) account
OWNER: ${{ github.event.repository.owner.name }} # This is the repository owner
REPOSITORY_NAME: ${{ github.event.repository.name }} # This is the repository name
jobs:
publish_docs_to_wiki:
name: Publish docs to Wiki
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
# 1. Clone the current wiki master branch to a folder named `tmp_wiki`
- name: Pull content from wiki
run: |
git config --global user.name "$USER_NAME"
git config --global user.email "$USER_NAME"@users.noreply.github.com
git clone https://"$USER_TOKEN"@github.com/SoftFever/"$REPOSITORY_NAME".wiki.git tmp_wiki
# 4. Synchronize differences between `doc` & `tmp_wiki`
# 5. Push new Wiki content
- name: Push main repo content to wiki
run: |
rsync -av --delete doc/ tmp_wiki/ --exclude .git
cd tmp_wiki
git add .
git commit -m "Updated Wiki content"
git push origin master

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Cache shellcheck download
id: cache-shellcheck-v0_11
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/shellcheck
key: ${{ runner.os }}-shellcheck-v0_11
@@ -36,7 +36,7 @@ jobs:
tar -xvf ~/sc.tar.xz -C ~
mv ~/shellcheck-"${INPUT_VERSION}"/shellcheck ~/shellcheck
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 1

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -1,368 +0,0 @@
name: Validate Documentation
on:
pull_request:
paths:
- 'src/slic3r/GUI/Tab.cpp'
- 'doc/**/*.md'
workflow_dispatch:
permissions:
contents: read
pull-requests: write
issues: write
jobs:
validate:
runs-on: windows-latest
name: Check Documentation
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v47
with:
files: |
src/slic3r/GUI/Tab.cpp
doc/**/*.md
- name: Run validation
if: steps.changed-files.outputs.any_changed == 'true'
shell: pwsh
run: |
# Helper Functions
function Normalize-Fragment($fragment) {
return $fragment.ToLower().Trim() -replace '[^a-z0-9\s-]', '' -replace ' ', '-' -replace '^-+|-+$', ''
}
function Add-BrokenReference($sourceFile, $line, $target, $issue, $type) {
return @{
SourceFile = $sourceFile
Line = $line
Target = $target
Issue = $issue
Type = $type
}
}
function Validate-Fragment($fragment, $availableAnchors, $sourceFile, $line, $target, $type) {
$cleanFragment = $fragment.StartsWith('#') ? $fragment.Substring(1) : $fragment
$normalizedFragment = Normalize-Fragment $cleanFragment
if ($availableAnchors -notcontains $normalizedFragment) {
return Add-BrokenReference $sourceFile $line $target "Fragment does not exist" $type
}
return $null
}
function Get-ImagesFromLine($line) {
$images = @()
$lineForParsing = [regex]::Replace($line, '`[^`]*`', '')
# Process markdown and HTML images
$imagePatterns = @(
@{ Pattern = "!\[([^\]]*)\]\(([^)]+)\)"; Type = "Markdown"; AltGroup = 1; UrlGroup = 2 }
@{ Pattern = '<img\s+[^>]*>'; Type = "HTML"; AltGroup = -1; UrlGroup = -1 }
)
foreach ($pattern in $imagePatterns) {
foreach ($match in [regex]::Matches($lineForParsing, $pattern.Pattern)) {
$altText = ""
$url = ""
if ($pattern.Type -eq "Markdown") {
$altText = $match.Groups[$pattern.AltGroup].Value
$url = $match.Groups[$pattern.UrlGroup].Value
} else {
# Extract from HTML
$imgTag = $match.Value
if ($imgTag -match 'alt\s*=\s*[`"'']([^`"'']*)[`"'']') { $altText = $matches[1] }
if ($imgTag -match 'src\s*=\s*[`"'']([^`"'']*)[`"'']') { $url = $matches[1] }
}
$images += @{
Match = $match.Value
Type = $pattern.Type
AltText = $altText
Url = $url
StartIndex = $match.Index
Length = $match.Length
}
}
}
return $images
}
# Initialize
$tabFile = Join-Path $PWD "src/slic3r/GUI/Tab.cpp"
$docDir = Join-Path $PWD 'doc'
$brokenReferences = @()
$docIndex = @{}
Write-Host "Validating documentation..." -ForegroundColor Blue
# Validate paths
$hasTabFile = Test-Path $tabFile
if (-not $hasTabFile) { Write-Host "::warning::Tab.cpp file not found at: $tabFile" }
if (-not (Test-Path $docDir)) { Write-Host "::error::doc folder does not exist"; exit 1 }
# Build documentation index
$mdFiles = Get-ChildItem -Path $docDir -Filter *.md -Recurse -File -ErrorAction SilentlyContinue
foreach ($mdFile in $mdFiles) {
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($mdFile.Name)
$relPath = (Resolve-Path $mdFile.FullName).Path.Substring($docDir.Length).TrimStart('\', '/')
$content = Get-Content -Path $mdFile.FullName -Encoding UTF8 -Raw
$lines = Get-Content -Path $mdFile.FullName -Encoding UTF8
# Extract anchors
$anchors = @()
$anchors += [regex]::Matches($content, '(?i)<a\s+[^>]*(?:name|id)\s*=\s*[`"'']([^`"'']+)[`"'']') |
ForEach-Object { $_.Groups[1].Value.ToLower() }
$anchors += [regex]::Matches($content, '(?m)^#+\s+(.+)$') |
ForEach-Object { Normalize-Fragment $_.Groups[1].Value.Trim() }
# Parse links
$links = @()
$inCodeFence = $false
for ($i = 0; $i -lt $lines.Count; $i++) {
$line = $lines[$i]
if ($line.TrimStart() -match '^(```|~~~)') {
$inCodeFence = -not $inCodeFence
continue
}
if ($inCodeFence) { continue }
$lineForParsing = [regex]::Replace($line, '`[^`]*`', '')
# Get all images from this line to skip them in link processing
$imagesInLine = Get-ImagesFromLine $line
$imageRanges = @()
foreach ($img in $imagesInLine) {
# Exclude the entire image syntax from link processing
$imageRanges += @{ Start = $img.StartIndex; End = $img.StartIndex + $img.Length }
}
# Find all markdown links, but exclude those that are part of images
foreach ($linkMatch in [regex]::Matches($lineForParsing, '(?<!!)\[([^\]]*)\]\(([^)]+)\)')) {
$linkStart = $linkMatch.Index
$linkEnd = $linkMatch.Index + $linkMatch.Length
# Check if this link overlaps with any image
$isPartOfImage = $false
foreach ($imageRange in $imageRanges) {
if (($linkStart -ge $imageRange.Start -and $linkStart -lt $imageRange.End) -or
($linkEnd -gt $imageRange.Start -and $linkEnd -le $imageRange.End) -or
($linkStart -le $imageRange.Start -and $linkEnd -ge $imageRange.End)) {
$isPartOfImage = $true
break
}
}
if ($isPartOfImage) { continue }
$linkText = $linkMatch.Groups[1].Value.Trim()
$destRaw = $linkMatch.Groups[2].Value.Trim()
# Handle internal fragments
if ($destRaw.StartsWith('#')) {
$fragment = $destRaw.Substring(1)
if ($fragment.Contains('#')) {
$brokenReferences += Add-BrokenReference $relPath ($i + 1) $destRaw "Internal link must use only one #." "Link"
} else {
$validationResult = Validate-Fragment $fragment $anchors $relPath ($i + 1) $destRaw "Link"
if ($validationResult) { $brokenReferences += $validationResult }
}
continue
}
# Skip external URLs
if ($destRaw -match '^(?:https?:|mailto:|data:|#|\\)') { continue }
# Check for double ##
if ($destRaw.Contains('##')) {
$brokenReferences += Add-BrokenReference $relPath ($i + 1) $destRaw "Use single # for fragments." "Link"
continue
}
# Parse file and fragment
$destParts = $destRaw -split '#', 2
$destNoFragment = $destParts[0]
$fragment = ($destParts.Length -gt 1) ? $destParts[1] : $null
if ($destNoFragment) {
$leaf = ($destNoFragment -split '[\\/]')[-1]
if ($leaf) {
$targetBase = $leaf.ToLower().EndsWith('.md') ? $leaf.Substring(0, $leaf.Length - 3) : $leaf
$targetBase = $targetBase.Trim()
if ($targetBase) {
$linkInfo = @{
TargetBase = $targetBase
Fragment = $fragment
Line = $i + 1
SourceFile = $relPath
}
$links += $linkInfo
}
}
}
}
}
$docIndex[$baseName] = @{ Anchors = $anchors; Links = $links }
}
# Parse Tab.cpp references
if ($hasTabFile) {
$regex = 'optgroup->append_single_option_line\s*\(\s*(?:"([^"]+)"|([^,]+?))\s*,\s*"([^"]+)"\s*\)'
$lines = Get-Content -Path $tabFile -Encoding UTF8
for ($i = 0; $i -lt $lines.Count; $i++) {
foreach ($match in [regex]::Matches($lines[$i], $regex)) {
$arg2Full = $match.Groups[3].Value.Trim()
if ($arg2Full.Contains('##')) {
$brokenReferences += Add-BrokenReference "Tab.cpp" ($i + 1) $arg2Full "Use single # for fragments." "Link"
continue
}
$arg2Parts = $arg2Full -split '#', 2
$docBase = $arg2Parts[0].Trim()
$fragment = ($arg2Parts.Length -gt 1) ? $arg2Parts[1].Trim() : $null
if (-not $docIndex.ContainsKey($docBase)) {
$brokenReferences += Add-BrokenReference "Tab.cpp" ($i + 1) $docBase "File does not exist" "Link"
} elseif ($fragment) {
$validationResult = Validate-Fragment $fragment $docIndex[$docBase].Anchors "Tab.cpp" ($i + 1) "$docBase#$fragment" "Link"
if ($validationResult) { $brokenReferences += $validationResult }
}
}
}
}
# Validate markdown links
foreach ($baseName in $docIndex.Keys) {
foreach ($link in $docIndex[$baseName].Links) {
if (-not $docIndex.ContainsKey($link.TargetBase)) {
$brokenReferences += Add-BrokenReference $link.SourceFile $link.Line "$($link.TargetBase).md" "File does not exist" "Link"
} elseif ($link.Fragment) {
$validationResult = Validate-Fragment $link.Fragment $docIndex[$link.TargetBase].Anchors $link.SourceFile $link.Line "$($link.TargetBase)#$($link.Fragment)" "Link"
if ($validationResult) { $brokenReferences += $validationResult }
}
}
}
# Validate images
Write-Host "Validating images..." -ForegroundColor Blue
$expectedUrlPattern = '^https://github\.com/SoftFever/OrcaSlicer/blob/main/([^?]+)\?raw=true$'
foreach ($file in $mdFiles) {
$lines = Get-Content $file.FullName -Encoding UTF8
$relPath = (Resolve-Path $file.FullName).Path.Substring($docDir.Length).TrimStart('\', '/')
$inCodeFence = $false
for ($lineNumber = 0; $lineNumber -lt $lines.Count; $lineNumber++) {
$line = $lines[$lineNumber]
if ($line.TrimStart() -match '^(```|~~~)') {
$inCodeFence = -not $inCodeFence
continue
}
if ($inCodeFence) { continue }
# Use the unified image detection function
$imagesInLine = Get-ImagesFromLine $line
foreach ($image in $imagesInLine) {
$altText = $image.AltText
$url = $image.Url
$imageMatch = $image.Match
$imageType = $image.Type
if (-not $altText.Trim() -and $url) {
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch "[$imageType] Missing alt text for image" "Image"
} elseif ($url -and $altText) {
# Validate URL format and file existence
if ($url -match $expectedUrlPattern) {
$relativePathInUrl = $matches[1]
$fileNameFromUrl = [System.IO.Path]::GetFileNameWithoutExtension($relativePathInUrl)
if ($altText -ne $fileNameFromUrl) {
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch "[$imageType] Alt text `"$altText`" ≠ filename `"$fileNameFromUrl`"" "Image"
}
$expectedImagePath = Join-Path $PWD ($relativePathInUrl -replace "/", "\")
if (-not (Test-Path $expectedImagePath)) {
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch "[$imageType] Image not found at path: $relativePathInUrl" "Image"
}
} else {
$urlIssues = @()
if (-not $url.StartsWith('https://github.com/SoftFever/OrcaSlicer/blob/main/')) { $urlIssues += "URL must start with expected prefix" }
if (-not $url.EndsWith('?raw=true')) { $urlIssues += "URL must end with '?raw=true'" }
if ($url -match '^https?://(?!github\.com/SoftFever/OrcaSlicer)') { $urlIssues += "External URLs not allowed" }
$issueText = "[$imageType] URL format issues: " + ($urlIssues -join '; ')
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch $issueText "Image"
}
}
}
}
}
# Report results
$linkErrors = $brokenReferences | Where-Object { $_.Type -eq "Link" }
$imageErrors = $brokenReferences | Where-Object { $_.Type -eq "Image" }
if ($brokenReferences.Count -gt 0) {
Write-Host "::error::Documentation validation failed"
# Build error summary for PR comment
$errorSummary = ""
# Report link errors
if ($linkErrors) {
Write-Host "::group::🔗 Link Validation Errors"
$errorSummary += "## 🔗 Link Validation Errors`n`n"
$linkErrors | Group-Object SourceFile | ForEach-Object {
Write-Host "📄 $($_.Name):" -ForegroundColor Yellow
$errorSummary += "**📄 doc/$($_.Name):**`n"
$_.Group | Sort-Object Line | ForEach-Object {
Write-Host " Line $($_.Line): $($_.Target) - $($_.Issue)" -ForegroundColor Red
Write-Host "::error file=doc/$($_.SourceFile),line=$($_.Line)::$($_.Target) - $($_.Issue)"
$errorSummary += "- Line $($_.Line): ``$($_.Target)`` - $($_.Issue)`n"
}
$errorSummary += "`n"
}
Write-Host "::endgroup::"
}
# Report image errors
if ($imageErrors) {
Write-Host "::group::🖼️ Image Validation Errors"
$errorSummary += "## 🖼️ Image Validation Errors`n`n"
$imageErrors | Group-Object SourceFile | ForEach-Object {
Write-Host "📄 $($_.Name):" -ForegroundColor Yellow
$errorSummary += "**📄 doc/$($_.Name):**`n"
$_.Group | Sort-Object Line | ForEach-Object {
Write-Host " Line $($_.Line): $($_.Issue)" -ForegroundColor Red
Write-Host "::error file=doc/$($_.SourceFile),line=$($_.Line)::$($_.Issue)"
$errorSummary += "- Line $($_.Line): $($_.Issue)`n"
}
$errorSummary += "`n"
}
Write-Host "::endgroup::"
}
# Export error summary for PR comment
Add-Content -Path $env:GITHUB_ENV -Value "VALIDATION_ERRORS<<EOF"
Add-Content -Path $env:GITHUB_ENV -Value $errorSummary
Add-Content -Path $env:GITHUB_ENV -Value "EOF"
exit 1
} else {
Write-Host "::notice::All documentation is valid!"
exit 0
}

1
.gitignore vendored
View File

@@ -42,3 +42,4 @@ deps_src/build/
test.js
/.cache/
.clangd
internal_docs/

View File

@@ -1,10 +1,20 @@
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "4.0")
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)
endif()
cmake_minimum_required(VERSION 3.13)
# Verify that your CMake version is exactly 3.31.x series or lower on windows
if ( ((MSVC) OR (WIN32)) AND (${CMAKE_VERSION} VERSION_GREATER_EQUAL "4.0") )
message(FATAL_ERROR "Only cmake versions between 3.13.x and 3.31.x is supported on windows. Detected version: ${CMAKE_VERSION}")
# Verify that your CMake version is exactly 3.5 series or higher on windows
if ( (MSVC OR WIN32) AND (${CMAKE_VERSION} VERSION_LESS "3.5") )
message(FATAL_ERROR "CMake current version ${CMAKE_VERSION} is too old. Minimum required is 3.5.")
endif()
# The following line used to be in tests/CMakeLists.txt
# Having it there causes rebuilds of all targets on any CMakeLists.txt change under tests/
# It has no effect on how code is compiled or linked.
# It just lets you later do `set_property(TARGET foo PROPERTY FOLDER "bar")`
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
# Detect known CI environments
set(IS_CI FALSE)
@@ -119,7 +129,7 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow performing desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.")
@@ -340,7 +350,7 @@ if(WIN32)
if (DEFINED ENV{WindowsSdkDir} AND DEFINED ENV{WindowsSDKVersion})
set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
else ()
set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22000.0")
set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.26100.0")
endif ()
if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
@@ -565,6 +575,7 @@ endif()
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
set(Boost_NO_SYSTEM_PATHS TRUE)
find_package(Boost 1.83.0 REQUIRED COMPONENTS system filesystem thread log log_setup locale regex chrono atomic date_time iostreams program_options nowide)
add_library(boost_libs INTERFACE)
@@ -682,6 +693,10 @@ find_package(PNG REQUIRED)
set(OpenGL_GL_PREFERENCE "LEGACY")
find_package(OpenGL REQUIRED)
if(APPLE AND CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set(OPENGL_LIBRARIES "-framework OpenGL" CACHE STRING "OpenGL framework" FORCE)
endif()
set(GLEW_ROOT "${CMAKE_PREFIX_PATH}")
message("GLEW_ROOT: ${GLEW_ROOT}")
# Find glew or use bundled version
@@ -716,7 +731,7 @@ add_custom_target(gettext_make_pot
)
add_custom_target(gettext_merge_po_with_pot
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Merge localization po with new generted pot file"
COMMENT "Merge localization po with new generated pot file"
)
file(GLOB BBL_L10N_PO_FILES "${BBL_L18N_DIR}/*/OrcaSlicer*.po")
foreach(po_file ${BBL_L10N_PO_FILES})
@@ -930,7 +945,7 @@ set (CPACK_PACKAGE_VERSION_MINOR "${ORCA_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${ORCA_VERSION_PATCH}")
set (CPACK_PACKAGE_FILE_NAME "OrcaSlicer_Windows_Installer_V${SoftFever_VERSION}")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Orca Slicer is an open source slicer for FDM printers")
set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/SoftFever/OrcaSlicer")
set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer/OrcaSlicer")
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set (CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/images\\\\OrcaSlicer.ico")
set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}")

View File

@@ -6,7 +6,7 @@
<a href="https://trendshift.io/repositories/952" target="_blank"><img src="https://trendshift.io/api/badge/repositories/952" alt="SoftFever%2FOrcaSlicer | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[![GitHub Repo stars](https://img.shields.io/github/stars/SoftFever/OrcaSlicer)](https://github.com/SoftFever/OrcaSlicer/stargazers) [![Build all](https://github.com/SoftFever/OrcaSlicer/actions/workflows/build_all.yml/badge.svg?branch=main)](https://github.com/SoftFever/OrcaSlicer/actions/workflows/build_all.yml)
[![GitHub Repo stars](https://img.shields.io/github/stars/OrcaSlicer/OrcaSlicer)](https://github.com/OrcaSlicer/OrcaSlicer/stargazers) [![Build all](https://github.com/OrcaSlicer/OrcaSlicer/actions/workflows/build_all.yml/badge.svg?branch=main)](https://github.com/OrcaSlicer/OrcaSlicer/actions/workflows/build_all.yml)
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
@@ -18,7 +18,7 @@ Optimize your prints with ultra-fast slicing, intelligent support generation, an
<a href="https://www.orcaslicer.com/" style="font-size:2em;">OrcaSlicer.com</a>
#### Github Repository:
<a href="https://github.com/SoftFever/OrcaSlicer"><img src="https://img.shields.io/badge/OrcaSlicer-181717?style=flat&logo=github&logoColor=white" width="200" alt="GitHub Logo"/> </a>
<a href="https://github.com/OrcaSlicer/OrcaSlicer"><img src="https://img.shields.io/badge/OrcaSlicer-181717?style=flat&logo=github&logoColor=white" width="200" alt="GitHub Logo"/> </a>
#### Follow us:
<a href="https://twitter.com/real_OrcaSlicer"><img src="https://img.shields.io/badge/real__OrcaSlicer-000000?style=flat&logo=x&logoColor=white" width="200" alt="X Logo"/> </a>
@@ -44,64 +44,62 @@ If you come across any of these in search results, please <b>report them</b> as
# Main features
- **[Advanced Calibration Tools](https://github.com/SoftFever/OrcaSlicer/wiki/Calibration)**
- **[Advanced Calibration Tools](https://www.orcaslicer.com/wiki/Calibration)**
Comprehensive suite: temperature towers, flow rate, retraction & more for optimal performance.
- **[Precise Wall](https://github.com/SoftFever/OrcaSlicer/wiki/quality_settings_precision#precise-wall) and [Seam Control](https://github.com/SoftFever/OrcaSlicer/wiki/quality_settings_seam)**
- **[Precise Wall](https://www.orcaslicer.com/wiki/quality_settings_precision#precise-wall) and [Seam Control](https://www.orcaslicer.com/wiki/quality_settings_seam)**
Adjust outer wall spacing and apply scarf seams to enhance print accuracy.
- **[Sandwich Mode](https://github.com/SoftFever/OrcaSlicer/wiki/quality_settings_wall_and_surfaces#innerouterinner) and [Polyholes](https://github.com/SoftFever/OrcaSlicer/wiki/quality_settings_precision#polyholes) Support**
Use varied infill [patterns](https://github.com/SoftFever/OrcaSlicer/wiki/strength_settings_patterns) and accurate hole shapes for improved clarity.
- **[Overhang](https://github.com/SoftFever/OrcaSlicer/wiki/quality_settings_overhangs) and [Support Optimization](https://github.com/SoftFever/OrcaSlicer/wiki#support-settings)**
- **[Sandwich Mode](https://www.orcaslicer.com/wiki/quality_settings_wall_and_surfaces#innerouterinner) and [Polyholes](https://www.orcaslicer.com/wiki/quality_settings_precision#polyholes) Support**
Use varied infill [patterns](https://www.orcaslicer.com/wiki/strength_settings_patterns) and accurate hole shapes for improved clarity.
- **[Overhang](https://www.orcaslicer.com/wiki/quality_settings_overhangs) and [Support Optimization](https://www.orcaslicer.com/wiki#support-settings)**
Modify geometry for printable overhangs with precise support placement.
- **[Granular Controls](https://github.com/SoftFever/OrcaSlicer/wiki#process-settings) and Customization**
- **[Granular Controls and Customization](https://www.orcaslicer.com/wiki#process-settings)**
Fine-tune print speed, layer height, pressure, and temperature with precision.
- **Network Printer Support**
Seamless integration with Klipper, PrusaLink, and OctoPrint for remote control.
- **[Mouse Ear Brims](https://github.com/SoftFever/OrcaSlicer/wiki/others_settings_brim) & Adaptive Bed Mesh**
- **[Mouse Ear Brims](https://www.orcaslicer.com/wiki/others_settings_brim) & [Adaptive Bed Mesh](https://www.orcaslicer.com/wiki/printer_basic_information_adaptive_bed_mesh)**
Automatic brims and adaptive mesh calibration ensure consistent adhesion.
- **User-Friendly Interface**
Intuitive drag-and-drop design with pre-made profiles for popular printers.
- **[Open-Source](https://github.com/SoftFever/OrcaSlicer) & [Community Driven](https://discord.gg/P4VE9UY9gJ)**
- **[Open-Source](https://github.com/OrcaSlicer/OrcaSlicer) & [Community Driven](https://discord.gg/P4VE9UY9gJ)**
Regular updates fueled by continuous community contributions.
- **Wide Printer Compatibility**
Supports a broad range of printers: Bambu Lab, Prusa, Creality, Voron, and more.
- Additional features can be found in the [change notes](https://github.com/SoftFever/OrcaSlicer/releases/).
- Additional features can be found in the [change notes](https://github.com/OrcaSlicer/OrcaSlicer/releases/).
# Wiki
The wiki below aims to provide a detailed explanation of the slicer settings, including how to maximize their use and how to calibrate and set up your printer.
The [wiki](https://www.orcaslicer.com/wiki) aims to provide a detailed explanation of the slicer settings, including how to maximize their use and how to calibrate and set up your printer.
Please note that the wiki is a work in progress. We appreciate your patience as we continue to develop and improve it!
- **[Access the wiki here](https://github.com/SoftFever/OrcaSlicer/wiki)**
- **[Contribute to the wiki](https://github.com/SoftFever/OrcaSlicer/wiki/How-to-wiki)**
- **[Access the wiki here](https://www.orcaslicer.com/wiki)**
- **[Contribute to the wiki](https://www.orcaslicer.com/wiki/How-to-wiki)**
# Download
## Stable Release
📥 **[Download the Latest Stable Release](https://github.com/SoftFever/OrcaSlicer/releases/latest)**
📥 **[Download the Latest Stable Release](https://github.com/OrcaSlicer/OrcaSlicer/releases/latest)**
Visit our GitHub Releases page for the latest stable version of OrcaSlicer, recommended for most users.
## Nightly Builds
🌙 **[Download the Latest Nightly Build](https://github.com/SoftFever/OrcaSlicer/releases/tag/nightly-builds)**
🌙 **[Download the Latest Nightly Build](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds)**
Explore the latest developments in OrcaSlicer with our nightly builds. Feedback on these versions is highly appreciated.
# How to install
## Windows
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/SoftFever/OrcaSlicer/releases).
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
- *For convenience there is also a portable build available.*
<details>
<summary>Troubleshooting</summary>
- *If you have troubles to run the build, you might need to install following runtimes:*
- [MicrosoftEdgeWebView2RuntimeInstallerX64](https://github.com/SoftFever/OrcaSlicer/releases/download/v1.0.10-sf2/MicrosoftEdgeWebView2RuntimeInstallerX64.exe)
- [MicrosoftEdgeWebView2RuntimeInstallerX64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/MicrosoftEdgeWebView2RuntimeInstallerX64.exe)
- [Details of this runtime](https://aka.ms/webview2)
- [Alternative Download Link Hosted by Microsoft](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
- [vcredist2019_x64](https://github.com/SoftFever/OrcaSlicer/releases/download/v1.0.10-sf2/vcredist2019_x64.exe)
- [vcredist2019_x64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/vcredist2019_x64.exe)
- [Alternative Download Link Hosted by Microsoft](https://aka.ms/vs/17/release/vc_redist.x64.exe)
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
</details>
@@ -146,7 +144,7 @@ winget install --id=SoftFever.OrcaSlicer -e
# How to Compile
All updated build instructions for Windows, macOS, and Linux are now available on the official [OrcaSlicer Wiki - How to build](https://github.com/SoftFever/OrcaSlicer/wiki/How-to-build) page.
All updated build instructions for Windows, macOS, and Linux are now available on the official [OrcaSlicer Wiki - How to build](https://www.orcaslicer.com/wiki/How-to-build) page.
Please refer to the wiki to ensure you're following the latest and most accurate steps for your platform.

View File

@@ -186,22 +186,22 @@ echo -e "${GREEN}All required dependencies found${NC}"
# Install runtime and SDK if requested
if [[ "$INSTALL_RUNTIME" == true ]]; then
echo -e "${YELLOW}Installing GNOME runtime and SDK...${NC}"
flatpak install --user -y flathub org.gnome.Platform//47
flatpak install --user -y flathub org.gnome.Sdk//47
flatpak install --user -y flathub org.gnome.Platform//48
flatpak install --user -y flathub org.gnome.Sdk//48
fi
# Check if required runtime is available
if ! flatpak info --user org.gnome.Platform//47 &> /dev/null; then
echo -e "${RED}Error: GNOME Platform 47 runtime is not installed.${NC}"
if ! flatpak info --user org.gnome.Platform//48 &> /dev/null; then
echo -e "${RED}Error: GNOME Platform 48 runtime is not installed.${NC}"
echo "Run with -i flag to install it automatically, or install manually:"
echo "flatpak install --user flathub org.gnome.Platform//47"
echo "flatpak install --user flathub org.gnome.Platform//48"
exit 1
fi
if ! flatpak info --user org.gnome.Sdk//47 &> /dev/null; then
echo -e "${RED}Error: GNOME SDK 47 is not installed.${NC}"
if ! flatpak info --user org.gnome.Sdk//48 &> /dev/null; then
echo -e "${RED}Error: GNOME SDK 48 is not installed.${NC}"
echo "Run with -i flag to install it automatically, or install manually:"
echo "flatpak install --user flathub org.gnome.Sdk//47"
echo "flatpak install --user flathub org.gnome.Sdk//48"
exit 1
fi

View File

@@ -27,7 +27,6 @@ function usage() {
echo " -L: use ld.lld as linker (if available)"
echo "For a first use, you want to './${SCRIPT_NAME} -u'"
echo " and then './${SCRIPT_NAME} -dsi'"
echo "To build with tests: './${SCRIPT_NAME} -st' or './${SCRIPT_NAME} -dst'"
}
SLIC3R_PRECOMPILED_HEADERS="ON"
@@ -102,11 +101,6 @@ if [ ${OPTIND} -eq 1 ] ; then
exit 1
fi
if [[ -n "${BUILD_TESTS}" ]] && [[ -z "${BUILD_ORCA}" ]] ; then
echo "-t flag requires -s flag in the same invocation"
exit 1
fi
function check_available_memory_and_disk() {
FREE_MEM_GB=$(free --gibi --total | grep 'Mem' | rev | cut --delimiter=" " --fields=1 | rev)
MIN_MEM_GB=10
@@ -221,7 +215,7 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
print_and_run cmake --build deps/$BUILD_DIR
fi
if [[ -n "${BUILD_ORCA}" ]] ; then
if [[ -n "${BUILD_ORCA}" ]] || [[ -n "${BUILD_TESTS}" ]] ; then
echo "Configuring OrcaSlicer..."
if [[ -n "${CLEAN_BUILD}" ]] ; then
print_and_run rm -fr $BUILD_DIR
@@ -243,11 +237,13 @@ if [[ -n "${BUILD_ORCA}" ]] ; then
"${COLORED_OUTPUT}" \
"${BUILD_ARGS[@]}"
echo "done"
echo "Building OrcaSlicer ..."
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer
echo "Building OrcaSlicer_profile_validator .."
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer_profile_validator
./scripts/run_gettext.sh
if [[ -n "${BUILD_ORCA}" ]]; then
echo "Building OrcaSlicer ..."
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer
echo "Building OrcaSlicer_profile_validator .."
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer_profile_validator
./scripts/run_gettext.sh
fi
if [[ -n "${BUILD_TESTS}" ]] ; then
echo "Building tests ..."
print_and_run cmake --build ${BUILD_DIR} --config "${BUILD_CONFIG}" --target tests/all

View File

@@ -3,7 +3,7 @@
set -e
set -o pipefail
while getopts ":dpa:snt:xbc:1h" opt; do
while getopts ":dpa:snt:xbc:1Th" opt; do
case "${opt}" in
d )
export BUILD_TARGET="deps"
@@ -37,6 +37,9 @@ while getopts ":dpa:snt:xbc:1h" opt; do
1 )
export CMAKE_BUILD_PARALLEL_LEVEL=1
;;
T )
export BUILD_TESTS="1"
;;
h ) echo "Usage: ./build_release_macos.sh [-d]"
echo " -d: Build deps only"
echo " -a: Set ARCHITECTURE (arm64 or x86_64 or universal)"
@@ -47,6 +50,7 @@ while getopts ":dpa:snt:xbc:1h" opt; do
echo " -b: Build without reconfiguring CMake"
echo " -c: Set CMake build configuration, default is Release"
echo " -1: Use single job for building"
echo " -T: Build and run tests"
exit 0
;;
* )
@@ -85,6 +89,15 @@ if [ -z "$OSX_DEPLOYMENT_TARGET" ]; then
export OSX_DEPLOYMENT_TARGET="11.3"
fi
CMAKE_VERSION=$(cmake --version | head -1 | sed 's/[^0-9]*\([0-9]*\).*/\1/')
if [ "$CMAKE_VERSION" -ge 4 ] 2>/dev/null; then
export CMAKE_POLICY_VERSION_MINIMUM=3.5
export CMAKE_POLICY_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
echo "Detected CMake 4.x, adding compatibility flag (env + cmake arg)"
else
export CMAKE_POLICY_COMPAT=""
fi
echo "Build params:"
echo " - ARCH: $ARCH"
echo " - BUILD_CONFIG: $BUILD_CONFIG"
@@ -133,7 +146,8 @@ function build_deps() {
-G "${DEPS_CMAKE_GENERATOR}" \
-DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \
-DCMAKE_OSX_ARCHITECTURES:STRING="${_ARCH}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}"
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
${CMAKE_POLICY_COMPAT}
fi
cmake --build . --config "$BUILD_CONFIG" --target deps
)
@@ -170,13 +184,24 @@ function build_slicer() {
-G "${SLICER_CMAKE_GENERATOR}" \
-DORCA_TOOLS=ON \
${ORCA_UPDATER_SIG_KEY:+-DORCA_UPDATER_SIG_KEY="$ORCA_UPDATER_SIG_KEY"} \
${BUILD_TESTS:+-DBUILD_TESTS=ON} \
-DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \
-DCMAKE_OSX_ARCHITECTURES="${_ARCH}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}"
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
${CMAKE_POLICY_COMPAT}
fi
cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET"
)
if [ "1." == "$BUILD_TESTS". ]; then
echo "Running tests for $_ARCH..."
(
set -x
cd "$PROJECT_BUILD_DIR"
ctest --build-config "$BUILD_CONFIG" --output-on-failure
)
fi
echo "Verify localization with gettext..."
(
cd "$PROJECT_DIR"

119
build_release_vs.bat Normal file
View File

@@ -0,0 +1,119 @@
@REM OrcaSlicer build script for Windows with VS auto-detect
@echo off
set WP=%CD%
@REM Detect Visual Studio version using msbuild
echo Detecting Visual Studio version using msbuild...
@REM Try to get MSBuild version - the output format varies by VS version
set VS_MAJOR=
for /f "tokens=*" %%i in ('msbuild -version 2^>^&1 ^| findstr /r "^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"') do (
for /f "tokens=1 delims=." %%a in ("%%i") do set VS_MAJOR=%%a
set MSBUILD_OUTPUT=%%i
goto :version_found
)
@REM Alternative method for newer MSBuild versions
if "%VS_MAJOR%"=="" (
for /f "tokens=*" %%i in ('msbuild -version 2^>^&1 ^| findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"') do (
for /f "tokens=1 delims=." %%a in ("%%i") do set VS_MAJOR=%%a
set MSBUILD_OUTPUT=%%i
goto :version_found
)
)
:version_found
echo MSBuild version detected: %MSBUILD_OUTPUT%
echo Major version: %VS_MAJOR%
if "%VS_MAJOR%"=="" (
echo Error: Could not determine Visual Studio version from msbuild
echo Please ensure Visual Studio and MSBuild are properly installed
exit /b 1
)
if "%VS_MAJOR%"=="16" (
set VS_VERSION=2019
set CMAKE_GENERATOR="Visual Studio 16 2019"
) else if "%VS_MAJOR%"=="17" (
set VS_VERSION=2022
set CMAKE_GENERATOR="Visual Studio 17 2022"
) else if "%VS_MAJOR%"=="18" (
set VS_VERSION=2026
set CMAKE_GENERATOR="Visual Studio 18 2026"
) else (
echo Error: Unsupported Visual Studio version: %VS_MAJOR%
echo Supported versions: VS2019 (16.x^), VS2022 (17.x^), VS2026 (18.x^)
exit /b 1
)
echo Detected Visual Studio %VS_VERSION% (version %VS_MAJOR%)
echo Using CMake generator: %CMAKE_GENERATOR%
@REM Pack deps
if "%1"=="pack" (
setlocal ENABLEDELAYEDEXPANSION
cd %WP%/deps/build
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do set build_date=%%c%%b%%a
echo packing deps: OrcaSlicer_dep_win64_!build_date!_vs!VS_VERSION!.zip
%WP%/tools/7z.exe a OrcaSlicer_dep_win64_!build_date!_vs!VS_VERSION!.zip OrcaSlicer_dep
exit /b 0
)
set debug=OFF
set debuginfo=OFF
if "%1"=="debug" set debug=ON
if "%2"=="debug" set debug=ON
if "%1"=="debuginfo" set debuginfo=ON
if "%2"=="debuginfo" set debuginfo=ON
if "%debug%"=="ON" (
set build_type=Debug
set build_dir=build-dbg
) else (
if "%debuginfo%"=="ON" (
set build_type=RelWithDebInfo
set build_dir=build-dbginfo
) else (
set build_type=Release
set build_dir=build
)
)
echo build type set to %build_type%
setlocal DISABLEDELAYEDEXPANSION
cd deps
mkdir %build_dir%
cd %build_dir%
set "SIG_FLAG="
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
if "%1"=="slicer" (
GOTO :slicer
)
echo "building deps.."
echo on
REM Set minimum CMake policy to avoid <3.5 errors
set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake ../ -G %CMAKE_GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=%build_type%
cmake --build . --config %build_type% --target deps -- -m
@echo off
if "%1"=="deps" exit /b 0
:slicer
echo "building Orca Slicer..."
cd %WP%
mkdir %build_dir%
cd %build_dir%
echo on
set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake .. -G %CMAKE_GENERATOR% -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
cmake --build . --config %build_type% --target ALL_BUILD -- -m
@echo off
cd ..
call scripts/run_gettext.bat
cd %build_dir%
cmake --build . --target install --config %build_type%

View File

@@ -46,6 +46,8 @@ if "%1"=="slicer" (
echo "building deps.."
echo on
REM Set minimum CMake policy to avoid <3.5 errors
set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake ../ -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=%build_type%
cmake --build . --config %build_type% --target deps -- -m
@echo off
@@ -59,6 +61,7 @@ mkdir %build_dir%
cd %build_dir%
echo on
set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake .. -G "Visual Studio 17 2022" -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
cmake --build . --config %build_type% --target ALL_BUILD -- -m
@echo off

View File

@@ -1,175 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
Catch
-----
This module defines a function to help use the Catch test framework.
The :command:`catch_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each Catch test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: catch_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
catch_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[TEST_LIST var]
)
``catch_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-names-only`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the Catch executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the Catch executable with the ``--list-test-names-only`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``catch_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``catch_discover_tests``.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``catch_discover_tests()``.
Note that this variable is only available in CTest.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX='${_TEST_PREFIX}'"
-D "TEST_SUFFIX='${_TEST_SUFFIX}'"
-D "TEST_LIST=${_TEST_LIST}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if (NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_CATCH_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
)

View File

@@ -1,106 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
macro(_add_catch_test_labels LINE)
# convert to list of tags
string(REPLACE "][" "]\\;[" tags ${line})
add_command(
set_tests_properties "${prefix}${test}${suffix}"
PROPERTIES
LABELS "${tags}"
)
endmacro()
macro(_add_catch_test LINE)
set(test ${line})
# use escape commas to handle properly test cases with commans inside the name
string(REPLACE "," "\\," test_name ${test})
# ...and add to script
add_command(
add_test "${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"${test_name}"
${extra_args}
)
add_command(
set_tests_properties "${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
)
list(APPEND tests "${prefix}${test}${suffix}")
endmacro()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
)
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
if(${result} EQUAL 0)
message(WARNING
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
)
elseif(${result} LESS 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
set(test)
set(tags_regex "(\\[([^\\[]*)\\])+$")
# Parse output
foreach(line ${output})
# lines without leading whitespaces are catch output not tests
if(${line} MATCHES "^[ \t]+")
# strip leading spaces and tabs
string(REGEX REPLACE "^[ \t]+" "" line ${line})
if(${line} MATCHES "${tags_regex}")
_add_catch_test_labels(${line})
else()
_add_catch_test(${line})
endif()
endif()
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@@ -3,7 +3,7 @@
# PrusaSlicer specifics:
# This file is backported from CMake 3.15 distribution to behave uniformly
# across all versions of CMake. It explicitly adds GLEW_STATIC complile
# across all versions of CMake. It explicitly adds GLEW_STATIC compile
# definition to static targets which is needed to prevent link errors.
#[=======================================================================[.rst:
@@ -223,8 +223,13 @@ if(NOT TARGET GLEW::glew AND NOT GLEW_USE_STATIC_LIBS)
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${GLEW_INCLUDE_DIRS}")
if(APPLE)
set_target_properties(GLEW::glew
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set_target_properties(GLEW::glew
PROPERTIES INTERFACE_LINK_LIBRARIES "-framework OpenGL")
else()
set_target_properties(GLEW::glew
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
endif()
endif()
if(GLEW_SHARED_LIBRARY_RELEASE)
@@ -258,8 +263,13 @@ elseif(NOT TARGET GLEW::glew_s AND GLEW_USE_STATIC_LIBS)
set_target_properties(GLEW::glew_s PROPERTIES INTERFACE_COMPILE_DEFINITIONS GLEW_STATIC)
if(APPLE)
set_target_properties(GLEW::glew_s
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set_target_properties(GLEW::glew_s
PROPERTIES INTERFACE_LINK_LIBRARIES "-framework OpenGL")
else()
set_target_properties(GLEW::glew_s
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
endif()
endif()
if(GLEW_STATIC_LIBRARY_RELEASE)
@@ -292,8 +302,13 @@ if(NOT TARGET GLEW::GLEW)
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${GLEW_INCLUDE_DIRS}")
if(APPLE)
set_target_properties(GLEW::GLEW
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set_target_properties(GLEW::GLEW
PROPERTIES INTERFACE_LINK_LIBRARIES "-framework OpenGL")
else()
set_target_properties(GLEW::GLEW
PROPERTIES INTERFACE_LINK_LIBRARIES OpenGL::GL)
endif()
endif()
if(TARGET GLEW::glew)

View File

@@ -61,7 +61,7 @@
#
# * TBB_FOUND - Set to false, or undefined, if we havent found, or
# dont want to use TBB.
# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
# * TBB_<component>_FOUND - If False, optional <component> part of TBB system is
# not available.
# * TBB_VERSION - The full version string
# * TBB_VERSION_MAJOR - The major version
@@ -77,7 +77,7 @@
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
# tbb_preview_debug.
#
# The following varibles should be used to build and link with TBB:
# The following variables should be used to build and link with TBB:
#
# * TBB_INCLUDE_DIRS - The include directory for TBB.
# * TBB_LIBRARIES - The libraries to link against to use TBB.

View File

@@ -133,7 +133,7 @@
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSAppTransportSecurity</key>
<dict>
<!-- Disable App Transport Security. Resolves https://github.com/SoftFever/OrcaSlicer/issues/791 -->
<!-- Disable App Transport Security. Resolves https://github.com/OrcaSlicer/OrcaSlicer/issues/791 -->
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>

View File

@@ -1,59 +0,0 @@
--- a/BGL/include/CGAL/boost/graph/iterator.h 2022-10-07 19:04:41 UTC
+++ b/BGL/include/CGAL/boost/graph/iterator.h
@@ -213,18 +213,7 @@ class Halfedge_around_source_iterator { (public)
{}
#ifndef DOXYGEN_RUNNING
- // design patter: "safe bool"
- // will be replaced by explicit operator bool with C++11
- typedef void (Halfedge_around_source_iterator::*bool_type)() const;
- void this_type_does_not_support_comparisons() const {}
-
- operator bool_type() const
- {
- return (! (this->base() == nullptr)) ?
- &Halfedge_around_source_iterator::this_type_does_not_support_comparisons : 0;
- }
-
bool operator==( const Self& i) const {
CGAL_assertion( anchor == anchor);
return ( g == i.g) && ( pos == i.pos) && ( winding == i.winding);
@@ -313,18 +302,7 @@ class Halfedge_around_target_iterator { (public)
{}
#ifndef DOXYGEN_RUNNING
- // design patter: "safe bool"
- // will be replaced by explicit operator bool with C++11
- typedef void (Halfedge_around_target_iterator::*bool_type)() const;
- void this_type_does_not_support_comparisons() const {}
-
- operator bool_type() const
- {
- return (! (this->base() == nullptr)) ?
- &Halfedge_around_target_iterator::this_type_does_not_support_comparisons : 0;
- }
-
bool operator==( const Self& i) const {
CGAL_assertion( anchor == anchor);
return ( g == i.g) && ( pos == i.pos) && ( winding == i.winding);
@@ -411,18 +389,6 @@ class Halfedge_around_face_iterator { (public)
const value_type& operator * ( ) const { return pos; }
pointer operator -> ( ) { return &pos; }
const value_type* operator -> ( ) const { return &pos; }
-
- // design patter: "safe bool"
- // will be replaced by explicit operator bool with C++11
- typedef void (Halfedge_around_face_iterator::*bool_type)() const;
-
- void this_type_does_not_support_comparisons() const {}
-
- operator bool_type() const
- {
- return (! (this->base() == nullptr)) ?
- &Halfedge_around_face_iterator::this_type_does_not_support_comparisons : 0;
- }
bool operator==( const Self& i) const {
CGAL_assertion( anchor == anchor);

View File

@@ -5,11 +5,10 @@ endif ()
orcaslicer_add_cmake_project(
CGAL
# GIT_REPOSITORY https://github.com/CGAL/cgal.git
# GIT_TAG bec70a6d52d8aacb0b3d82a7b4edc3caa899184b # releases/CGAL-5.0
# GIT_TAG 3654f780ae0c64675cabaef0e5ddaf904c48b4b7 # releases/CGAL-5.6.3
# For whatever reason, this keeps downloading forever (repeats downloads if finished)
URL https://github.com/CGAL/cgal/archive/refs/tags/v5.4.zip
URL_HASH SHA256=d7605e0a5a5ca17da7547592f6f6e4a59430a0bc861948974254d0de43eab4c0
PATCH_COMMAND git apply ${CGAL_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-clang19.patch
URL https://github.com/CGAL/cgal/releases/download/v5.6.3/CGAL-5.6.3.zip
URL_HASH SHA256=5d577acb4a9918ccb960491482da7a3838f8d363aff47e14d703f19fd84733d4
DEPENDS dep_Boost dep_GMP dep_MPFR
)

10
deps/CMakeLists.txt vendored
View File

@@ -1,3 +1,7 @@
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "4.0")
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)
endif()
#
# This CMake project downloads, configures and builds OrcaSlicer dependencies on Unix and Windows.
#
@@ -16,8 +20,8 @@
# On Windows, architecture (64 vs 32 bits) is judged based on the compiler variant.
# To build dependencies for either 64 or 32 bit OS, use the respective compiler command line.
#
# WARNING: On UNIX platforms wxWidgets hardcode the destdir path into its `wx-conffig` utility,
# therefore, unfortunatelly, the installation cannot be copied/moved elsewhere without re-installing wxWidgets.
# WARNING: On UNIX platforms wxWidgets hardcode the destdir path into its `wx-config` utility,
# therefore, unfortunately, the installation cannot be copied/moved elsewhere without re-installing wxWidgets.
#
cmake_minimum_required(VERSION 3.2)
@@ -177,6 +181,7 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
-DCMAKE_MODULE_PATH:STRING=${PROJECT_SOURCE_DIR}/../cmake/modules
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
@@ -221,6 +226,7 @@ else()
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
-DBUILD_SHARED_LIBS:BOOL=OFF

View File

@@ -25,7 +25,8 @@ else ()
endif ()
ExternalProject_Add(dep_MPFR
URL https://www.mpfr.org/mpfr-4.2.2/mpfr-4.2.2.tar.bz2
URL https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.2.tar.bz2
https://www.mpfr.org/mpfr-4.2.2/mpfr-4.2.2.tar.bz2
URL_HASH SHA256=9ad62c7dc910303cd384ff8f1f4767a655124980bb6d8650fe62c815a231bb7b
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/MPFR
BUILD_IN_SOURCE ON

View File

@@ -1,15 +1,21 @@
From 6fb3f6333150a777e835fc7c48e49750591bf7fe Mon Sep 17 00:00:00 2001
From: Benjamin Buch <bebuch@users.noreply.github.com>
Date: Thu, 23 May 2024 16:05:19 +0200
Subject: [PATCH] Support VS 2022 17.1x.y
With 17.10.0 the MSVC toolset was set to 19.40.x which breaks the compatibility test in the OpenCV's CMake Config files.
---
cmake/templates/OpenCVConfig.root-WIN32.cmake.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake
index 7f229cde96..d8e20f8fe9 100644
--- a/cmake/OpenCVDetectCXXCompiler.cmake
+++ b/cmake/OpenCVDetectCXXCompiler.cmake
@@ -171,8 +171,10 @@ elseif(MSVC)
set(OpenCV_RUNTIME vc15)
elseif(MSVC_VERSION MATCHES "^192[0-9]$")
set(OpenCV_RUNTIME vc16)
- elseif(MSVC_VERSION MATCHES "^193[0-9]$")
+ elseif(MSVC_VERSION MATCHES "^19[34][0-9]$")
set(OpenCV_RUNTIME vc17)
+ elseif(MSVC_VERSION MATCHES "^195[0-9]$")
+ set(OpenCV_RUNTIME vc18)
else()
message(WARNING "OpenCV does not recognize MSVC_VERSION \"${MSVC_VERSION}\". Cannot set OpenCV_RUNTIME")
endif()
diff --git a/cmake/templates/OpenCVConfig.root-WIN32.cmake.in b/cmake/templates/OpenCVConfig.root-WIN32.cmake.in
index b0f254ebe8..62e36272f3 100644
index b0f254ebe8..8f0178b0ae 100644
--- a/cmake/templates/OpenCVConfig.root-WIN32.cmake.in
+++ b/cmake/templates/OpenCVConfig.root-WIN32.cmake.in
@@ -137,7 +137,7 @@ elseif(MSVC)
@@ -21,32 +27,28 @@ index b0f254ebe8..62e36272f3 100644
set(OpenCV_RUNTIME vc17)
check_one_config(has_VS2022)
if(NOT has_VS2022)
--
2.45.2.windows.1
From f85818ba6f9031c450475a7453dee0acce31a881 Mon Sep 17 00:00:00 2001
From: Benjamin Buch <bebuch@users.noreply.github.com>
Date: Fri, 24 May 2024 11:10:09 +0200
Subject: [PATCH] Support VS 2022 17.1x.y in OpenCVDetectCXXCompiler.cmake
With 17.10.0 the MSVC toolset was set to 19.40.x which breaks the compatibility test in the OpenCV's CMake Config files.
---
cmake/OpenCVDetectCXXCompiler.cmake | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake
index 1743aca11f..448afd46ea 100644
--- a/cmake/OpenCVDetectCXXCompiler.cmake
+++ b/cmake/OpenCVDetectCXXCompiler.cmake
@@ -176,7 +176,7 @@ elseif(MSVC)
set(OpenCV_RUNTIME vc15)
elseif(MSVC_VERSION MATCHES "^192[0-9]$")
set(OpenCV_RUNTIME vc16)
- elseif(MSVC_VERSION MATCHES "^193[0-9]$")
+ elseif(MSVC_VERSION MATCHES "^19[34][0-9]$")
set(OpenCV_RUNTIME vc17)
else()
message(WARNING "OpenCV does not recognize MSVC_VERSION \"${MSVC_VERSION}\". Cannot set OpenCV_RUNTIME")
--
2.45.2.windows.1
@@ -151,6 +151,24 @@ elseif(MSVC)
endif()
endif()
endif()
+ elseif(MSVC_VERSION MATCHES "^195[0-9]$")
+ set(OpenCV_RUNTIME vc18)
+ check_one_config(has_VS2026)
+ if(NOT has_VS2026)
+ set(OpenCV_RUNTIME vc17)
+ check_one_config(has_VS2022)
+ if(NOT has_VS2022)
+ set(OpenCV_RUNTIME vc16)
+ check_one_config(has_VS2019)
+ if(NOT has_VS2019)
+ set(OpenCV_RUNTIME vc15) # selecting previous compatible runtime version
+ check_one_config(has_VS2017)
+ if(NOT has_VS2017)
+ set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version
+ endif()
+ endif()
+ endif()
+ endif()
endif()
elseif(MINGW)
set(OpenCV_RUNTIME mingw)

View File

@@ -11,7 +11,7 @@ endif ()
orcaslicer_add_cmake_project(OpenCV
URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz
URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs2022.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
CMAKE_ARGS
-DBUILD_SHARED_LIBS=0
-DBUILD_PERE_TESTS=OFF

View File

@@ -1,11 +1,11 @@
include(GNUInstallDirs)
orcaslicer_add_cmake_project(Qhull
URL "https://github.com/qhull/qhull/archive/v8.0.1.zip"
URL_HASH SHA256=5287f5edd6a0372588f5d6640799086a4033d89d19711023ef8229dd9301d69b
URL "https://github.com/qhull/qhull/archive/v8.0.2.zip"
URL_HASH SHA256=a378e9a39e718e289102c20d45632f873bfdc58a7a5f924246ea4b176e185f1e
CMAKE_ARGS
-DINCLUDE_INSTALL_DIR=${CMAKE_INSTALL_INCLUDEDIR}
)
if (MSVC)
add_debug_dep(dep_Qhull)
endif ()
endif ()

View File

@@ -19,6 +19,10 @@ elseif (MSVC_VERSION LESS 1950)
# 1930-1949 = VS 17.0 (v143 toolset)
set(DEP_VS_VER "17")
set(DEP_BOOST_TOOLSET "msvc-14.3")
elseif (MSVC_VERSION LESS 1960)
# 1950-1959 = VS 18.0 (v145 toolset)
set(DEP_VS_VER "18")
set(DEP_BOOST_TOOLSET "msvc-14.5")
else ()
message(FATAL_ERROR "Unsupported MSVC version")
endif ()

View File

@@ -24,6 +24,14 @@ else ()
set(_wx_edge "-DwxUSE_WEBVIEW_EDGE=OFF")
endif ()
set(_wx_opengl_override "")
if(APPLE AND CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set(_wx_opengl_override
-DOPENGL_gl_LIBRARY="-framework OpenGL"
-DOPENGL_glu_LIBRARY="-framework OpenGL"
)
endif()
orcaslicer_add_cmake_project(
wxWidgets
GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets"
@@ -31,6 +39,7 @@ orcaslicer_add_cmake_project(
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG}
${_wx_flatpak_patch}
CMAKE_ARGS
${_wx_opengl_override}
-DwxBUILD_PRECOMP=ON
${_wx_toolkit}
"-DCMAKE_DEBUG_POSTFIX:STRING=${_wx_debug_postfix}"

View File

@@ -10,13 +10,13 @@ add_subdirectory(earcut)
add_subdirectory(fast_float)
add_subdirectory(nanosvg)
add_subdirectory(nlohmann)
add_subdirectory(spline) # Header-only spline library
add_subdirectory(stb_dxt) # Header-only STB DXT compression library
# Static libraries
add_subdirectory(Shiny)
add_subdirectory(admesh)
add_subdirectory(clipper)
add_subdirectory(clipper2)
add_subdirectory(expat)
add_subdirectory(glu-libtess)
add_subdirectory(hidapi)

View File

@@ -24,18 +24,7 @@ target_link_libraries(your_target PRIVATE semver::semver)
target_link_libraries(your_target PRIVATE hints)
```
### 3. **spline** (Interface Library)
- **Type**: Interface library (header-only)
- **Target**: `spline` or `spline::spline`
- **Headers**: `spline.h`
- **Usage**:
```cmake
target_link_libraries(your_target PRIVATE spline)
# or
target_link_libraries(your_target PRIVATE spline::spline)
```
### 4. **stb_dxt** (Interface Library)
### 3. **stb_dxt** (Interface Library)
- **Type**: Interface library (header-only)
- **Target**: `stb_dxt` or `stb_dxt::stb_dxt`
- **Headers**: `stb_dxt.h`
@@ -53,10 +42,9 @@ target_link_libraries(your_target PRIVATE stb_dxt::stb_dxt)
1. **In your CMakeLists.txt**, simply link the library:
```cmake
add_executable(my_app main.cpp)
target_link_libraries(my_app
PRIVATE
target_link_libraries(my_app
PRIVATE
semver::semver # For version parsing
spline::spline # For spline interpolation
stb_dxt::stb_dxt # For DXT texture compression
hints # For hints functionality
)
@@ -67,9 +55,6 @@ target_link_libraries(my_app
// For semver
#include <semver.h>
// For spline
#include <spline.h>
// For stb_dxt
#include <stb_dxt.h>
@@ -100,7 +85,6 @@ target_link_libraries(mycomponent
PUBLIC
semver::semver # Version handling is part of public API
PRIVATE
spline::spline # Used internally for interpolation
stb_dxt::stb_dxt # Used internally for texture compression
)

View File

@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.10)
project(Clipper2 VERSION 1.5.2 LANGUAGES C CXX)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS OFF)
option(BUILD_SHARED_LIBS "Build shared libs" OFF)
include(GNUInstallDirs)
set(CLIPPER2_INC
Clipper2Lib/include/clipper2/clipper.h
Clipper2Lib/include/clipper2/clipper.core.h
Clipper2Lib/include/clipper2/clipper.engine.h
Clipper2Lib/include/clipper2/clipper.export.h
Clipper2Lib/include/clipper2/clipper.minkowski.h
Clipper2Lib/include/clipper2/clipper.offset.h
Clipper2Lib/include/clipper2/clipper.rectclip.h
Clipper2Lib/include/clipper2/clipper2_z.hpp
)
set(CLIPPER2_SRC
Clipper2Lib/src/clipper.engine.cpp
Clipper2Lib/src/clipper.offset.cpp
Clipper2Lib/src/clipper.rectclip.cpp
Clipper2Lib/src/clipper2_z.cpp
)
# 2d version of Clipper2
add_library(Clipper2 ${CLIPPER2_INC} ${CLIPPER2_SRC})
target_include_directories(Clipper2
PUBLIC Clipper2Lib/include
)
if (WIN32)
target_compile_options(Clipper2 PRIVATE /W4 /WX)
else()
target_compile_options(Clipper2 PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(Clipper2 PUBLIC -lm)
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 14.1)
target_compile_options(Clipper2 PRIVATE -Wno-error=template-id-cdtor)
endif()
endif()
set_target_properties(Clipper2 PROPERTIES FOLDER Libraries
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER "${CLIPPER2_INC}"
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,646 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 17 September 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_ENGINE_H
#define CLIPPER_ENGINE_H
#include "clipper2/clipper.core.h"
#include <queue>
#include <functional>
#include <memory>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
struct Scanline;
struct IntersectNode;
struct Active;
struct Vertex;
struct LocalMinima;
struct OutRec;
struct HorzSegment;
//Note: all clipping operations except for Difference are commutative.
enum class ClipType { NoClip, Intersection, Union, Difference, Xor };
enum class PathType { Subject, Clip };
enum class JoinWith { NoJoin, Left, Right };
enum class VertexFlags : uint32_t {
Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
};
constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
{
return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
}
constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b)
{
return (enum VertexFlags)(uint32_t(a) | uint32_t(b));
}
struct Vertex {
Point64 pt;
Vertex* next = nullptr;
Vertex* prev = nullptr;
VertexFlags flags = VertexFlags::Empty;
};
struct OutPt {
Point64 pt;
OutPt* next = nullptr;
OutPt* prev = nullptr;
OutRec* outrec;
HorzSegment* horz = nullptr;
OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) {
next = this;
prev = this;
}
};
class PolyPath;
class PolyPath64;
class PolyPathD;
using PolyTree64 = PolyPath64;
using PolyTreeD = PolyPathD;
struct OutRec;
typedef std::vector<OutRec*> OutRecList;
//OutRec: contains a path in the clipping solution. Edges in the AEL will
//have OutRec pointers assigned when they form part of the clipping solution.
struct OutRec {
size_t idx = 0;
OutRec* owner = nullptr;
Active* front_edge = nullptr;
Active* back_edge = nullptr;
OutPt* pts = nullptr;
PolyPath* polypath = nullptr;
OutRecList* splits = nullptr;
OutRec* recursive_split = nullptr;
Rect64 bounds = {};
Path64 path;
bool is_open = false;
~OutRec() {
if (splits) delete splits;
// nb: don't delete the split pointers
// as these are owned by ClipperBase's outrec_list_
};
};
///////////////////////////////////////////////////////////////////
//Important: UP and DOWN here are premised on Y-axis positive down
//displays, which is the orientation used in Clipper's development.
///////////////////////////////////////////////////////////////////
struct Active {
Point64 bot;
Point64 top;
int64_t curr_x = 0; //current (updated at every new scanline)
double dx = 0.0;
int wind_dx = 1; //1 or -1 depending on winding direction
int wind_cnt = 0;
int wind_cnt2 = 0; //winding count of the opposite polytype
OutRec* outrec = nullptr;
//AEL: 'active edge list' (Vatti's AET - active edge table)
// a linked list of all edges (from left to right) that are present
// (or 'active') within the current scanbeam (a horizontal 'beam' that
// sweeps from bottom to top over the paths in the clipping operation).
Active* prev_in_ael = nullptr;
Active* next_in_ael = nullptr;
//SEL: 'sorted edge list' (Vatti's ST - sorted table)
// linked list used when sorting edges into their new positions at the
// top of scanbeams, but also (re)used to process horizontals.
Active* prev_in_sel = nullptr;
Active* next_in_sel = nullptr;
Active* jump = nullptr;
Vertex* vertex_top = nullptr;
LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
bool is_left_bound = false;
JoinWith join_with = JoinWith::NoJoin;
};
struct LocalMinima {
Vertex* vertex;
PathType polytype;
bool is_open;
LocalMinima(Vertex* v, PathType pt, bool open) :
vertex(v), polytype(pt), is_open(open){}
};
struct IntersectNode {
Point64 pt;
Active* edge1;
Active* edge2;
IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {}
IntersectNode(Active* e1, Active* e2, Point64& pt_) :
pt(pt_), edge1(e1), edge2(e2) {}
};
struct HorzSegment {
OutPt* left_op;
OutPt* right_op = nullptr;
bool left_to_right = true;
HorzSegment() : left_op(nullptr) { }
explicit HorzSegment(OutPt* op) : left_op(op) { }
};
struct HorzJoin {
OutPt* op1 = nullptr;
OutPt* op2 = nullptr;
HorzJoin() {};
explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { }
};
#ifdef USINGZ
typedef std::function<void(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
typedef std::function<void(const PointD& e1bot, const PointD& e1top,
const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD;
#endif
typedef std::vector<HorzSegment> HorzSegmentList;
typedef std::unique_ptr<LocalMinima> LocalMinima_ptr;
typedef std::vector<LocalMinima_ptr> LocalMinimaList;
typedef std::vector<IntersectNode> IntersectNodeList;
// ReuseableDataContainer64 ------------------------------------------------
class ReuseableDataContainer64 {
private:
friend class ClipperBase;
LocalMinimaList minima_list_;
std::vector<Vertex*> vertex_lists_;
void AddLocMin(Vertex& vert, PathType polytype, bool is_open);
public:
virtual ~ReuseableDataContainer64();
void Clear();
void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
};
// ClipperBase -------------------------------------------------------------
class ClipperBase {
private:
ClipType cliptype_ = ClipType::NoClip;
FillRule fillrule_ = FillRule::EvenOdd;
FillRule fillpos = FillRule::Positive;
int64_t bot_y_ = 0;
bool minima_list_sorted_ = false;
bool using_polytree_ = false;
Active* actives_ = nullptr;
Active *sel_ = nullptr;
LocalMinimaList minima_list_; //pointers in case of memory reallocs
LocalMinimaList::iterator current_locmin_iter_;
std::vector<Vertex*> vertex_lists_;
std::priority_queue<int64_t> scanline_list_;
IntersectNodeList intersect_nodes_;
HorzSegmentList horz_seg_list_;
std::vector<HorzJoin> horz_join_list_;
void Reset();
inline void InsertScanline(int64_t y);
inline bool PopScanline(int64_t &y);
inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima);
void DisposeAllOutRecs();
void DisposeVerticesAndLocalMinima();
void DeleteEdges(Active*& e);
inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open);
bool IsContributingClosed(const Active &e) const;
inline bool IsContributingOpen(const Active &e) const;
void SetWindCountForClosedPathEdge(Active &edge);
void SetWindCountForOpenPathEdge(Active &e);
void InsertLocalMinimaIntoAEL(int64_t bot_y);
void InsertLeftEdge(Active &e);
inline void PushHorz(Active &e);
inline bool PopHorz(Active *&e);
inline OutPt* StartOpenPath(Active &e, const Point64& pt);
inline void UpdateEdgeIntoAEL(Active *e);
void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
inline void DeleteFromAEL(Active &e);
inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
void DoIntersections(const int64_t top_y);
void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y);
bool BuildIntersectList(const int64_t top_y);
void ProcessIntersectList();
void SwapPositionsInAEL(Active& edge1, Active& edge2);
OutRec* NewOutRec();
OutPt* AddOutPt(const Active &e, const Point64& pt);
OutPt* AddLocalMinPoly(Active &e1, Active &e2,
const Point64& pt, bool is_new = false);
OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
void DoHorizontal(Active &horz);
bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex,
int64_t &horz_left, int64_t &horz_right);
void DoTopOfScanbeam(const int64_t top_y);
Active *DoMaxima(Active &e);
void JoinOutrecPaths(Active &e1, Active &e2);
void FixSelfIntersects(OutRec* outrec);
void DoSplitOp(OutRec* outRec, OutPt* splitOp);
inline void AddTrialHorzJoin(OutPt* op);
void ConvertHorzSegsToJoins();
void ProcessHorzJoins();
void Split(Active& e, const Point64& pt);
inline void CheckJoinLeft(Active& e,
const Point64& pt, bool check_curr_x = false);
inline void CheckJoinRight(Active& e,
const Point64& pt, bool check_curr_x = false);
protected:
bool preserve_collinear_ = true;
bool reverse_solution_ = false;
int error_code_ = 0;
bool has_open_paths_ = false;
bool succeeded_ = true;
OutRecList outrec_list_; //pointers in case list memory reallocated
bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
void CleanCollinear(OutRec* outrec);
bool CheckBounds(OutRec* outrec);
bool CheckSplitOwner(OutRec* outrec, OutRecList* splits);
void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
#ifdef USINGZ
ZCallback64 zCallback_ = nullptr;
void SetZ(const Active& e1, const Active& e2, Point64& pt);
#endif
void CleanUp(); // unlike Clear, CleanUp preserves added paths
void AddPath(const Path64& path, PathType polytype, bool is_open);
void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
public:
virtual ~ClipperBase();
int ErrorCode() const { return error_code_; };
void PreserveCollinear(bool val) { preserve_collinear_ = val; };
bool PreserveCollinear() const { return preserve_collinear_;};
void ReverseSolution(bool val) { reverse_solution_ = val; };
bool ReverseSolution() const { return reverse_solution_; };
void Clear();
void AddReuseableData(const ReuseableDataContainer64& reuseable_data);
#ifdef USINGZ
int64_t DefaultZ = 0;
#endif
};
// PolyPath / PolyTree --------------------------------------------------------
//PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned
//by clipping operations. While this structure is more complex than the
//alternative Paths structure, it does preserve path 'ownership' - ie those
//paths that contain (or own) other paths. This will be useful to some users.
class PolyPath {
protected:
PolyPath* parent_;
public:
PolyPath(PolyPath* parent = nullptr): parent_(parent){}
virtual ~PolyPath() {};
//https://en.cppreference.com/w/cpp/language/rule_of_three
PolyPath(const PolyPath&) = delete;
PolyPath& operator=(const PolyPath&) = delete;
unsigned Level() const
{
unsigned result = 0;
const PolyPath* p = parent_;
while (p) { ++result; p = p->parent_; }
return result;
}
virtual PolyPath* AddChild(const Path64& path) = 0;
virtual void Clear() = 0;
virtual size_t Count() const { return 0; }
const PolyPath* Parent() const { return parent_; }
bool IsHole() const
{
unsigned lvl = Level();
//Even levels except level 0
return lvl && !(lvl & 1);
}
template<typename T>
static double Clipper2LibArea(const Path<T> &poly)
{
#ifdef USINGZ
return Clipper2Lib_Z::Area<T>(poly);
#else
return Clipper2Lib::Area<T>(poly);
#endif
}
};
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList;
class PolyPath64 : public PolyPath {
private:
PolyPath64List childs_;
Path64 polygon_;
public:
explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }
~PolyPath64() {
childs_.resize(0);
}
PolyPath64* operator [] (size_t index) const
{
return childs_[index].get(); //std::unique_ptr
}
PolyPath64* Child(size_t index) const
{
return childs_[index].get();
}
PolyPath64List::const_iterator begin() const { return childs_.cbegin(); }
PolyPath64List::const_iterator end() const { return childs_.cend(); }
PolyPath64* AddChild(const Path64& path) override
{
return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
}
void Clear() override
{
childs_.resize(0);
}
size_t Count() const override
{
return childs_.size();
}
const Path64& Polygon() const { return polygon_; };
double Area() const
{
return std::accumulate(childs_.cbegin(), childs_.cend(), Clipper2LibArea<int64_t>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
};
class PolyPathD : public PolyPath {
private:
PolyPathDList childs_;
double scale_;
PathD polygon_;
public:
explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
}
explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
int error_code = 0;
polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
}
explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
{
scale_ = parent ? parent->scale_ : 1.0;
polygon_ = path;
}
~PolyPathD() {
childs_.resize(0);
}
PolyPathD* operator [] (size_t index) const
{
return childs_[index].get();
}
PolyPathD* Child(size_t index) const
{
return childs_[index].get();
}
PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
PolyPathDList::const_iterator end() const { return childs_.cend(); }
void SetScale(double value) { scale_ = value; }
double Scale() const { return scale_; }
PolyPathD* AddChild(const Path64& path) override
{
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}
PolyPathD* AddChild(const PathD& path)
{
return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
}
void Clear() override
{
childs_.resize(0);
}
size_t Count() const override
{
return childs_.size();
}
const PathD& Polygon() const { return polygon_; };
double Area() const
{
return std::accumulate(childs_.begin(), childs_.end(), Clipper2LibArea<double>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
};
class Clipper64 : public ClipperBase
{
private:
void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen);
void BuildTree64(PolyPath64& polytree, Paths64& open_paths);
public:
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback_ = cb; }
#endif
void AddSubject(const Paths64& subjects)
{
AddPaths(subjects, PathType::Subject, false);
}
void AddOpenSubject(const Paths64& open_subjects)
{
AddPaths(open_subjects, PathType::Subject, true);
}
void AddClip(const Paths64& clips)
{
AddPaths(clips, PathType::Clip, false);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, Paths64& closed_paths)
{
Paths64 dummy;
return Execute(clip_type, fill_rule, closed_paths, dummy);
}
bool Execute(ClipType clip_type, FillRule fill_rule,
Paths64& closed_paths, Paths64& open_paths)
{
closed_paths.clear();
open_paths.clear();
if (ExecuteInternal(clip_type, fill_rule, false))
BuildPaths64(closed_paths, &open_paths);
CleanUp();
return succeeded_;
}
bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree)
{
Paths64 dummy;
return Execute(clip_type, fill_rule, polytree, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths)
{
if (ExecuteInternal(clip_type, fill_rule, true))
{
open_paths.clear();
polytree.Clear();
BuildTree64(polytree, open_paths);
}
CleanUp();
return succeeded_;
}
};
class ClipperD : public ClipperBase {
private:
double scale_ = 1.0, invScale_ = 1.0;
#ifdef USINGZ
ZCallbackD zCallbackD_ = nullptr;
#endif
void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen);
void BuildTreeD(PolyPathD& polytree, PathsD& open_paths);
public:
explicit ClipperD(int precision = 2) : ClipperBase()
{
CheckPrecisionRange(precision, error_code_);
// to optimize scaling / descaling precision
// set the scale to a power of double's radix (2) (#25)
scale_ = std::pow(std::numeric_limits<double>::radix,
std::ilogb(std::pow(10, precision)) + 1);
invScale_ = 1 / scale_;
}
#ifdef USINGZ
void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; };
void ZCB(const Point64& e1bot, const Point64& e1top,
const Point64& e2bot, const Point64& e2top, Point64& pt)
{
// de-scale (x & y)
// temporarily convert integers to their initial float values
// this will slow clipping marginally but will make it much easier
// to understand the coordinates passed to the callback function
PointD tmp = PointD(pt) * invScale_;
PointD e1b = PointD(e1bot) * invScale_;
PointD e1t = PointD(e1top) * invScale_;
PointD e2b = PointD(e2bot) * invScale_;
PointD e2t = PointD(e2top) * invScale_;
zCallbackD_(e1b,e1t, e2b, e2t, tmp);
pt.z = tmp.z; // only update 'z'
};
void CheckCallback()
{
if(zCallbackD_)
// if the user defined float point callback has been assigned
// then assign the proxy callback function
ClipperBase::zCallback_ =
std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
else
ClipperBase::zCallback_ = nullptr;
}
#endif
void AddSubject(const PathsD& subjects)
{
AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false);
}
void AddOpenSubject(const PathsD& open_subjects)
{
AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true);
}
void AddClip(const PathsD& clips)
{
AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false);
}
bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
{
PathsD dummy;
return Execute(clip_type, fill_rule, closed_paths, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths)
{
#ifdef USINGZ
CheckCallback();
#endif
if (ExecuteInternal(clip_type, fill_rule, false))
{
BuildPathsD(closed_paths, &open_paths);
}
CleanUp();
return succeeded_;
}
bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree)
{
PathsD dummy;
return Execute(clip_type, fill_rule, polytree, dummy);
}
bool Execute(ClipType clip_type,
FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths)
{
#ifdef USINGZ
CheckCallback();
#endif
if (ExecuteInternal(clip_type, fill_rule, true))
{
polytree.Clear();
polytree.SetScale(invScale_);
open_paths.clear();
BuildTreeD(polytree, open_paths);
}
CleanUp();
return succeeded_;
}
};
} // namespace
#endif // CLIPPER_ENGINE_H

View File

@@ -0,0 +1,836 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 24 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
/*
Boolean clipping:
cliptype: NoClip=0, Intersection=1, Union=2, Difference=3, Xor=4
fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
Polygon offsetting (inflate/deflate):
jointype: Square=0, Bevel=1, Round=2, Miter=3
endtype: Polygon=0, Joined=1, Butt=2, Square=3, Round=4
The path structures used extensively in other parts of this library are all
based on std::vector classes. Since C++ classes can't be accessed by other
languages, these paths are exported here as very simple array structures
(either of int64_t or double) that can be parsed by just about any
programming language.
These 2D paths are defined by series of x and y coordinates together with an
optional user-defined 'z' value (see Z-values below). Hence, a vertex refers
to a single x and y coordinate (+/- a user-defined value). Data structures
have names with suffixes that indicate the array type (either int64_t or
double). For example, the data structure CPath64 contains an array of int64_t
values, whereas the data structure CPathD contains an array of double.
Where documentation omits the type suffix (eg CPath), it is referring to an
array whose data type could be either int64_t or double.
For conciseness, the following letters are used in the diagrams below:
N: Number of vertices in a given path
C: Count (ie number) of paths (or PolyPaths) in the structure
A: Number of elements in an array
CPath64 and CPathD:
These are arrays of either int64_t or double values. Apart from
the first two elements, these arrays are a series of vertices
that together define a path. The very first element contains the
number of vertices (N) in the path, while second element should
contain a 0 value.
_______________________________________________________________
| counters | vertex1 | vertex2 | ... | vertexN |
| N, 0 | x1, y1, (z1) | x2, y2, (z2) | ... | xN, yN, (zN) |
---------------------------------------------------------------
CPaths64 and CPathsD:
These are also arrays of either int64_t or double values that
contain any number of consecutive CPath structures. However,
preceding the first path is a pair of values. The first value
contains the length of the entire array structure (A), and the
second contains the number (ie count) of contained paths (C).
Memory allocation for CPaths64 = A * sizeof(int64_t)
Memory allocation for CPathsD = A * sizeof(double)
__________________________________________
| counters | path1 | path2 | ... | pathC |
| A, C | | | ... | |
------------------------------------------
CPolytree64 and CPolytreeD:
The entire polytree structure is an array of int64_t or double. The
first element in the array indicates the array's total length (A).
The second element indicates the number (C) of CPolyPath structures
that are the TOP LEVEL CPolyPath in the polytree, and these top
level CPolyPath immediately follow these first two array elements.
These top level CPolyPath structures may, in turn, contain nested
CPolyPath children, and these collectively make a tree structure.
_________________________________________________________
| counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC |
| A, C | | | ... | |
---------------------------------------------------------
CPolyPath64 and CPolyPathD:
These array structures consist of a pair of counter values followed by a
series of polygon vertices and a series of nested CPolyPath children.
The first counter values indicates the number of vertices in the
polygon (N), and the second counter indicates the CPolyPath child count (C).
_____________________________________________________________________________
|cntrs |vertex1 |vertex2 |...|vertexN |child1|child2|...|childC|
|N, C |x1, y1, (z1)| x2, y2, (z2)|...|xN, yN, (zN)| | |...| |
-----------------------------------------------------------------------------
DisposeArray64 & DisposeArrayD:
All array structures are allocated in heap memory which will eventually
need to be released. However, since applications linking to these DLL
functions may use different memory managers, the only safe way to release
this memory is to use the exported DisposeArray functions.
(Optional) Z-Values:
Structures will only contain user-defined z-values when the USINGZ
pre-processor identifier is used. The library does not assign z-values
because this field is intended for users to assign custom values to vertices.
Z-values in input paths (subject and clip) will be copied to solution paths.
New vertices at path intersections will generate a callback event that allows
users to assign z-values at these new vertices. The user's callback function
must conform with the DLLZCallback definition and be registered with the
DLL via SetZCallback. To assist the user in assigning z-values, the library
passes in the callback function the new intersection point together with
the four vertices that define the two segments that are intersecting.
*/
#ifndef CLIPPER2_EXPORT_H
#define CLIPPER2_EXPORT_H
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
#include <cstdlib>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
typedef int64_t* CPath64;
typedef int64_t* CPaths64;
typedef double* CPathD;
typedef double* CPathsD;
typedef int64_t* CPolyPath64;
typedef int64_t* CPolyTree64;
typedef double* CPolyPathD;
typedef double* CPolyTreeD;
template <typename T>
struct CRect {
T left;
T top;
T right;
T bottom;
};
typedef CRect<int64_t> CRect64;
typedef CRect<double> CRectD;
template <typename T>
inline bool CRectIsEmpty(const CRect<T>& rect)
{
return (rect.right <= rect.left) || (rect.bottom <= rect.top);
}
template <typename T>
inline Rect<T> CRectToRect(const CRect<T>& rect)
{
Rect<T> result;
result.left = rect.left;
result.top = rect.top;
result.right = rect.right;
result.bottom = rect.bottom;
return result;
}
template <typename T1, typename T2>
inline T1 Reinterpret(T2 value) {
return *reinterpret_cast<T1*>(&value);
}
#ifdef _WIN32
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define EXTERN_DLL_EXPORT extern "C"
#endif
//////////////////////////////////////////////////////
// EXPORTED FUNCTION DECLARATIONS
//////////////////////////////////////////////////////
EXTERN_DLL_EXPORT const char* Version();
EXTERN_DLL_EXPORT void DisposeArray64(int64_t*& p)
{
delete[] p;
}
EXTERN_DLL_EXPORT void DisposeArrayD(double*& p)
{
delete[] p;
}
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPolyTree64& sol_tree, CPaths64& solution_open,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPathsD& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPolyTreeD& solution, CPathsD& solution_open, int precision = 2,
bool preserve_collinear = true, bool reverse_solution = false);
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path,
double delta, uint8_t jointype, uint8_t endtype,
double miter_limit = 2.0, double arc_tolerance = 0.0,
bool reverse_solution = false);
EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path,
double delta, uint8_t jointype, uint8_t endtype,
int precision = 2, double miter_limit = 2.0,
double arc_tolerance = 0.0, bool reverse_solution = false);
// RectClip & RectClipLines:
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect,
const CPaths64 paths);
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect,
const CPathsD paths, int precision = 2);
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
const CPaths64 paths);
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision = 2);
//////////////////////////////////////////////////////
// INTERNAL FUNCTIONS
//////////////////////////////////////////////////////
#ifdef USINGZ
ZCallback64 dllCallback64 = nullptr;
ZCallbackD dllCallbackD = nullptr;
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 3;
#else
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 2;
#endif
template <typename T>
static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
size_t& cnt, size_t& array_len)
{
array_len = 2;
cnt = 0;
for (const Path<T>& path : paths)
if (path.size())
{
array_len += path.size() * EXPORT_VERTEX_DIMENSIONALITY + 2;
++cnt;
}
}
static size_t GetPolyPathArrayLen64(const PolyPath64& pp)
{
size_t result = 2; // poly_length + child_count
result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY;
//plus nested children :)
for (size_t i = 0; i < pp.Count(); ++i)
result += GetPolyPathArrayLen64(*pp[i]);
return result;
}
static size_t GetPolyPathArrayLenD(const PolyPathD& pp)
{
size_t result = 2; // poly_length + child_count
result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY;
//plus nested children :)
for (size_t i = 0; i < pp.Count(); ++i)
result += GetPolyPathArrayLenD(*pp[i]);
return result;
}
static void GetPolytreeCountAndCStorageSize64(const PolyTree64& tree,
size_t& cnt, size_t& array_len)
{
cnt = tree.Count(); // nb: top level count only
array_len = GetPolyPathArrayLen64(tree);
}
static void GetPolytreeCountAndCStorageSizeD(const PolyTreeD& tree,
size_t& cnt, size_t& array_len)
{
cnt = tree.Count(); // nb: top level count only
array_len = GetPolyPathArrayLenD(tree);
}
template <typename T>
static T* CreateCPathsFromPathsT(const Paths<T>& paths)
{
size_t cnt = 0, array_len = 0;
GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
T* result = new T[array_len], * v = result;
*v++ = array_len;
*v++ = cnt;
for (const Path<T>& path : paths)
{
if (!path.size()) continue;
*v++ = path.size();
*v++ = 0;
for (const Point<T>& pt : path)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
*v++ = Reinterpret<T>(pt.z);
#endif
}
}
return result;
}
CPathsD CreateCPathsDFromPathsD(const PathsD& paths)
{
if (!paths.size()) return nullptr;
size_t cnt, array_len;
GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
CPathsD result = new double[array_len], v = result;
*v++ = (double)array_len;
*v++ = (double)cnt;
for (const PathD& path : paths)
{
if (!path.size()) continue;
*v = (double)path.size();
++v; *v++ = 0;
for (const PointD& pt : path)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<double>(pt.z);
#endif
}
}
return result;
}
CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
{
if (!paths.size()) return nullptr;
size_t cnt, array_len;
GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
CPathsD result = new double[array_len], v = result;
*v++ = (double)array_len;
*v++ = (double)cnt;
for (const Path64& path : paths)
{
if (!path.size()) continue;
*v = (double)path.size();
++v; *v++ = 0;
for (const Point64& pt : path)
{
*v++ = pt.x * scale;
*v++ = pt.y * scale;
#ifdef USINGZ
*v++ = Reinterpret<double>(pt.z);
#endif
}
}
return result;
}
template <typename T>
static Path<T> ConvertCPathToPathT(T* path)
{
Path<T> result;
if (!path) return result;
T* v = path;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip 0 value
result.reserve(cnt);
for (size_t j = 0; j < cnt; ++j)
{
T x = *v++, y = *v++;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
result.emplace_back(x, y);
#endif
}
return result;
}
template <typename T>
static Paths<T> ConvertCPathsToPathsT(T* paths)
{
Paths<T> result;
if (!paths) return result;
T* v = paths; ++v;
size_t cnt = static_cast<size_t>(*v++);
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
Path<T> path;
path.reserve(cnt2);
for (size_t j = 0; j < cnt2; ++j)
{
T x = *v++, y = *v++;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
path.emplace_back(x, y, z);
#else
path.emplace_back(x, y);
#endif
}
result.emplace_back(std::move(path));
}
return result;
}
static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale)
{
Path64 result;
if (!path) return result;
double* v = path;
size_t cnt = static_cast<size_t>(*v);
v += 2; // skip 0 value
result.reserve(cnt);
for (size_t j = 0; j < cnt; ++j)
{
double x = *v++ * scale;
double y = *v++ * scale;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
result.emplace_back(x, y);
#endif
}
return result;
}
static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
{
Paths64 result;
if (!paths) return result;
double* v = paths;
++v; // skip the first value (0)
size_t cnt = static_cast<size_t>(*v++);
result.reserve(cnt);
for (size_t i = 0; i < cnt; ++i)
{
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
Path64 path;
path.reserve(cnt2);
for (size_t j = 0; j < cnt2; ++j)
{
double x = *v++ * scale;
double y = *v++ * scale;
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
path.emplace_back(x, y, z);
#else
path.emplace_back(x, y);
#endif
}
result.emplace_back(std::move(path));
}
return result;
}
static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v)
{
*v++ = static_cast<int64_t>(pp->Polygon().size());
*v++ = static_cast<int64_t>(pp->Count());
for (const Point64& pt : pp->Polygon())
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<int64_t>(pt.z); // raw memory copy
#endif
}
for (size_t i = 0; i < pp->Count(); ++i)
CreateCPolyPath64(pp->Child(i), v);
}
static void CreateCPolyPathD(const PolyPathD* pp, double*& v)
{
*v++ = static_cast<double>(pp->Polygon().size());
*v++ = static_cast<double>(pp->Count());
for (const PointD& pt : pp->Polygon())
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
* v++ = Reinterpret<double>(pt.z); // raw memory copy
#endif
}
for (size_t i = 0; i < pp->Count(); ++i)
CreateCPolyPathD(pp->Child(i), v);
}
static int64_t* CreateCPolyTree64(const PolyTree64& tree)
{
size_t cnt, array_len;
GetPolytreeCountAndCStorageSize64(tree, cnt, array_len);
if (!cnt) return nullptr;
// allocate storage
int64_t* result = new int64_t[array_len];
int64_t* v = result;
*v++ = static_cast<int64_t>(array_len);
*v++ = static_cast<int64_t>(tree.Count());
for (size_t i = 0; i < tree.Count(); ++i)
CreateCPolyPath64(tree.Child(i), v);
return result;
}
static double* CreateCPolyTreeD(const PolyTreeD& tree)
{
double scale = std::log10(tree.Scale());
size_t cnt, array_len;
GetPolytreeCountAndCStorageSizeD(tree, cnt, array_len);
if (!cnt) return nullptr;
// allocate storage
double* result = new double[array_len];
double* v = result;
*v++ = static_cast<double>(array_len);
*v++ = static_cast<double>(tree.Count());
for (size_t i = 0; i < tree.Count(); ++i)
CreateCPolyPathD(tree.Child(i), v);
return result;
}
//////////////////////////////////////////////////////
// EXPORTED FUNCTION DEFINITIONS
//////////////////////////////////////////////////////
EXTERN_DLL_EXPORT const char* Version()
{
return CLIPPER2_VERSION;
}
EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPaths64& solution, CPaths64& solution_open,
bool preserve_collinear, bool reverse_solution)
{
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
Clipper64 clipper;
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallback64)
clipper.SetZCallback(dllCallback64);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPathsFromPathsT(sol);
solution_open = CreateCPathsFromPathsT(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype,
uint8_t fillrule, const CPaths64 subjects,
const CPaths64 subjects_open, const CPaths64 clips,
CPolyTree64& sol_tree, CPaths64& solution_open,
bool preserve_collinear, bool reverse_solution)
{
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
Paths64 sub, sub_open, clp, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
PolyTree64 tree;
Clipper64 clipper;
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallback64)
clipper.SetZCallback(dllCallback64);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open))
return -1; // clipping bug - should never happen :)
sol_tree = CreateCPolyTree64(tree);
solution_open = CreateCPathsFromPathsT(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPathsD& solution, CPathsD& solution_open, int precision,
bool preserve_collinear, bool reverse_solution)
{
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
//const double scale = std::pow(10, precision);
PathsD sub, sub_open, clp, sol, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
ClipperD clipper(precision);
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallbackD)
clipper.SetZCallback(dllCallbackD);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype),
FillRule(fillrule), sol, sol_open)) return -1;
solution = CreateCPathsDFromPathsD(sol);
solution_open = CreateCPathsDFromPathsD(sol_open);
return 0;
}
EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
uint8_t fillrule, const CPathsD subjects,
const CPathsD subjects_open, const CPathsD clips,
CPolyTreeD& solution, CPathsD& solution_open, int precision,
bool preserve_collinear, bool reverse_solution)
{
if (precision < -8 || precision > 8) return -5;
if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
//double scale = std::pow(10, precision);
int err = 0;
PathsD sub, sub_open, clp, sol_open;
sub = ConvertCPathsToPathsT(subjects);
sub_open = ConvertCPathsToPathsT(subjects_open);
clp = ConvertCPathsToPathsT(clips);
PolyTreeD tree;
ClipperD clipper(precision);
clipper.PreserveCollinear(preserve_collinear);
clipper.ReverseSolution(reverse_solution);
#ifdef USINGZ
if (dllCallbackD)
clipper.SetZCallback(dllCallbackD);
#endif
if (sub.size() > 0) clipper.AddSubject(sub);
if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
if (clp.size() > 0) clipper.AddClip(clp);
if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open))
return -1; // clipping bug - should never happen :)
solution = CreateCPolyTreeD(tree);
solution_open = CreateCPathsDFromPathsD(sol_open);
return 0; //success !!
}
EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
double delta, uint8_t jointype, uint8_t endtype, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
Paths64 pp;
pp = ConvertCPathsToPathsT(paths);
ClipperOffset clip_offset( miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
double delta, uint8_t jointype, uint8_t endtype,
int precision, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
if (precision < -8 || precision > 8 || !paths) return nullptr;
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
Paths64 pp = ConvertCPathsDToPaths64(paths, scale);
clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta * scale, result);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path,
double delta, uint8_t jointype, uint8_t endtype, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
Path64 pp;
pp = ConvertCPathToPathT(path);
ClipperOffset clip_offset(miter_limit,
arc_tolerance, reverse_solution);
clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta, result);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path,
double delta, uint8_t jointype, uint8_t endtype,
int precision, double miter_limit,
double arc_tolerance, bool reverse_solution)
{
if (precision < -8 || precision > 8 || !path) return nullptr;
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
Path64 pp = ConvertCPathDToPath64WithScale(path, scale);
clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype));
Paths64 result;
clip_offset.Execute(delta * scale, result);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r64 = CRectToRect(rect);
class RectClip64 rc(r64);
Paths64 pp = ConvertCPathsToPathsT(paths);
Paths64 result = rc.Execute(pp);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
RectD r = CRectToRect(rect);
Rect64 rec = ScaleRect<int64_t, double>(r, scale);
Paths64 pp = ConvertCPathsDToPaths64(paths, scale);
class RectClip64 rc(rec);
Paths64 result = rc.Execute(pp);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect,
const CPaths64 paths)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
Rect64 r = CRectToRect(rect);
class RectClipLines64 rcl (r);
Paths64 pp = ConvertCPathsToPathsT(paths);
Paths64 result = rcl.Execute(pp);
return CreateCPathsFromPathsT(result);
}
EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
const CPathsD paths, int precision)
{
if (CRectIsEmpty(rect) || !paths) return nullptr;
if (precision < -8 || precision > 8) return nullptr;
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
class RectClipLines64 rcl(r);
Paths64 pp = ConvertCPathsDToPaths64(paths, scale);
Paths64 result = rcl.Execute(pp);
return CreateCPathsDFromPaths64(result, 1 / scale);
}
EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPathToPathT(cpath);
Path64 pattern = ConvertCPathToPathT(cpattern);
Paths64 solution = MinkowskiSum(pattern, path, is_closed);
return CreateCPathsFromPathsT(solution);
}
EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
{
Path64 path = ConvertCPathToPathT(cpath);
Path64 pattern = ConvertCPathToPathT(cpattern);
Paths64 solution = MinkowskiDiff(pattern, path, is_closed);
return CreateCPathsFromPathsT(solution);
}
#ifdef USINGZ
typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt);
typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt);
EXTERN_DLL_EXPORT void SetZCallback64(DLLZCallback64 callback)
{
dllCallback64 = callback;
}
EXTERN_DLL_EXPORT void SetZCallbackD(DLLZCallbackD callback)
{
dllCallbackD = callback;
}
#endif
}
#endif // CLIPPER2_EXPORT_H

View File

@@ -0,0 +1,770 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 27 April 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_H
#define CLIPPER_H
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.minkowski.h"
#include "clipper2/clipper.rectclip.h"
#include <type_traits>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
const Paths64& subjects, const Paths64& clips)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, result);
return result;
}
inline void BooleanOp(ClipType cliptype, FillRule fillrule,
const Paths64& subjects, const Paths64& clips, PolyTree64& solution)
{
Paths64 sol_open;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, solution, sol_open);
}
inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule,
const PathsD& subjects, const PathsD& clips, int precision = 2)
{
int error_code = 0;
CheckPrecisionRange(precision, error_code);
PathsD result;
if (error_code) return result;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, result);
return result;
}
inline void BooleanOp(ClipType cliptype, FillRule fillrule,
const PathsD& subjects, const PathsD& clips,
PolyTreeD& polytree, int precision = 2)
{
polytree.Clear();
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (error_code) return;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.AddClip(clips);
clipper.Execute(cliptype, fillrule, polytree);
}
inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
}
inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Union, fillrule, subjects, clips);
}
inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2)
{
PathsD result;
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (error_code) return result;
ClipperD clipper(precision);
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Difference, fillrule, subjects, clips);
}
inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
{
return BooleanOp(ClipType::Xor, fillrule, subjects, clips);
}
inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
{
return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec);
}
inline Paths64 InflatePaths(const Paths64& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0,
double arc_tolerance = 0.0)
{
if (!delta) return paths;
ClipperOffset clip_offset(miter_limit, arc_tolerance);
clip_offset.AddPaths(paths, jt, et);
Paths64 solution;
clip_offset.Execute(delta, solution);
return solution;
}
inline PathsD InflatePaths(const PathsD& paths, double delta,
JoinType jt, EndType et, double miter_limit = 2.0,
int precision = 2, double arc_tolerance = 0.0)
{
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (!delta) return paths;
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance);
clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
if (error_code) return PathsD();
Paths64 solution;
clip_offset.Execute(delta * scale, solution);
return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
}
template <typename T>
inline Path<T> TranslatePath(const Path<T>& path, T dx, T dy)
{
Path<T> result;
result.reserve(path.size());
std::transform(path.begin(), path.end(), back_inserter(result),
[dx, dy](const auto& pt) { return Point<T>(pt.x + dx, pt.y +dy); });
return result;
}
inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
{
return TranslatePath<int64_t>(path, dx, dy);
}
inline PathD TranslatePath(const PathD& path, double dx, double dy)
{
return TranslatePath<double>(path, dx, dy);
}
template <typename T>
inline Paths<T> TranslatePaths(const Paths<T>& paths, T dx, T dy)
{
Paths<T> result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
return result;
}
inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
{
return TranslatePaths<int64_t>(paths, dx, dy);
}
inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy)
{
return TranslatePaths<double>(paths, dx, dy);
}
inline Paths64 RectClip(const Rect64& rect, const Paths64& paths)
{
if (rect.IsEmpty() || paths.empty()) return Paths64();
RectClip64 rc(rect);
return rc.Execute(paths);
}
inline Paths64 RectClip(const Rect64& rect, const Path64& path)
{
if (rect.IsEmpty() || path.empty()) return Paths64();
RectClip64 rc(rect);
return rc.Execute(Paths64{ path });
}
inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2)
{
if (rect.IsEmpty() || paths.empty()) return PathsD();
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClip64 rc(r);
Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
if (error_code) return PathsD(); // ie: error_code result is lost
return ScalePaths<double, int64_t>(
rc.Execute(pp), 1 / scale, error_code);
}
inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2)
{
return RectClip(rect, PathsD{ path }, precision);
}
inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines)
{
if (rect.IsEmpty() || lines.empty()) return Paths64();
RectClipLines64 rcl(rect);
return rcl.Execute(lines);
}
inline Paths64 RectClipLines(const Rect64& rect, const Path64& line)
{
return RectClipLines(rect, Paths64{ line });
}
inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
{
if (rect.IsEmpty() || lines.empty()) return PathsD();
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
Rect64 r = ScaleRect<int64_t, double>(rect, scale);
RectClipLines64 rcl(r);
Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code);
if (error_code) return PathsD();
p = rcl.Execute(p);
return ScalePaths<double, int64_t>(p, 1 / scale, error_code);
}
inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2)
{
return RectClipLines(rect, PathsD{ line }, precision);
}
namespace details
{
inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
{
paths.emplace_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPaths64(*child, paths);
}
inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
{
paths.emplace_back(polypath.Polygon());
for (const auto& child : polypath)
PolyPathToPathsD(*child, paths);
}
inline bool PolyPath64ContainsChildren(const PolyPath64& pp)
{
for (const auto& child : pp)
{
// return false if this child isn't fully contained by its parent
// checking for a single vertex outside is a bit too crude since
// it doesn't account for rounding errors. It's better to check
// for consecutive vertices found outside the parent's polygon.
int outsideCnt = 0;
for (const Point64& pt : child->Polygon())
{
PointInPolygonResult result = PointInPolygon(pt, pp.Polygon());
if (result == PointInPolygonResult::IsInside) --outsideCnt;
else if (result == PointInPolygonResult::IsOutside) ++outsideCnt;
if (outsideCnt > 1) return false;
else if (outsideCnt < -1) break;
}
// now check any nested children too
if (child->Count() > 0 && !PolyPath64ContainsChildren(*child))
return false;
}
return true;
}
static void OutlinePolyPath(std::ostream& os,
size_t idx, bool isHole, size_t count, const std::string& preamble)
{
std::string plural = (count == 1) ? "." : "s.";
if (isHole)
os << preamble << "+- Hole (" << idx << ") contains " << count <<
" nested polygon" << plural << std::endl;
else
os << preamble << "+- Polygon (" << idx << ") contains " << count <<
" hole" << plural << std::endl;
}
static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp,
size_t idx, std::string preamble)
{
OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
for (size_t i = 0; i < pp.Count(); ++i)
if (pp.Child(i)->Count())
details::OutlinePolyPath64(os, *pp.Child(i), i, preamble + " ");
}
static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp,
size_t idx, std::string preamble)
{
OutlinePolyPath(os, idx, pp.IsHole(), pp.Count(), preamble);
for (size_t i = 0; i < pp.Count(); ++i)
if (pp.Child(i)->Count())
details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + " ");
}
template<typename T, typename U>
inline constexpr void MakePathGeneric(const T an_array,
size_t array_size, std::vector<U>& result)
{
result.reserve(array_size / 2);
for (size_t i = 0; i < array_size; i +=2)
#ifdef USINGZ
result.emplace_back( an_array[i], an_array[i + 1], 0 );
#else
result.emplace_back( an_array[i], an_array[i + 1] );
#endif
}
} // end details namespace
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
{
std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
for (size_t i = 0; i < pp.Count(); ++i)
if (pp.Child(i)->Count())
details::OutlinePolyPath64(os, *pp.Child(i), i, " ");
os << std::endl << std::endl;
return os;
}
inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp)
{
std::string plural = (pp.Count() == 1) ? " polygon." : " polygons.";
os << std::endl << "Polytree with " << pp.Count() << plural << std::endl;
for (size_t i = 0; i < pp.Count(); ++i)
if (pp.Child(i)->Count())
details::OutlinePolyPathD(os, *pp.Child(i), i, " ");
os << std::endl << std::endl;
if (!pp.Level()) os << std::endl;
return os;
}
inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree)
{
Paths64 result;
for (const auto& child : polytree)
details::PolyPathToPaths64(*child, result);
return result;
}
inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree)
{
PathsD result;
for (const auto& child : polytree)
details::PolyPathToPathsD(*child, result);
return result;
}
inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
{
for (const auto& child : polytree)
if (child->Count() > 0 &&
!details::PolyPath64ContainsChildren(*child))
return false;
return true;
}
template<typename T,
typename std::enable_if<
std::is_integral<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline Path64 MakePath(const std::vector<T>& list)
{
const auto size = list.size() - list.size() % 2;
if (list.size() != size)
DoError(non_pair_error_i); // non-fatal without exception handling
Path64 result;
details::MakePathGeneric(list, size, result);
return result;
}
template<typename T, std::size_t N,
typename std::enable_if<
std::is_integral<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline Path64 MakePath(const T(&list)[N])
{
// Make the compiler error on unpaired value (i.e. no runtime effects).
static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
Path64 result;
details::MakePathGeneric(list, N, result);
return result;
}
template<typename T,
typename std::enable_if<
std::is_arithmetic<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline PathD MakePathD(const std::vector<T>& list)
{
const auto size = list.size() - list.size() % 2;
if (list.size() != size)
DoError(non_pair_error_i); // non-fatal without exception handling
PathD result;
details::MakePathGeneric(list, size, result);
return result;
}
template<typename T, std::size_t N,
typename std::enable_if<
std::is_arithmetic<T>::value &&
!std::is_same<char, T>::value, bool
>::type = true>
inline PathD MakePathD(const T(&list)[N])
{
// Make the compiler error on unpaired value (i.e. no runtime effects).
static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
PathD result;
details::MakePathGeneric(list, N, result);
return result;
}
#ifdef USINGZ
template<typename T2, std::size_t N>
inline Path64 MakePathZ(const T2(&list)[N])
{
static_assert(N % 3 == 0 && std::numeric_limits<T2>::is_integer,
"MakePathZ requires integer values in multiples of 3");
std::size_t size = N / 3;
Path64 result(size);
for (size_t i = 0; i < size; ++i)
result[i] = Point64(list[i * 3],
list[i * 3 + 1], list[i * 3 + 2]);
return result;
}
template<typename T2, std::size_t N>
inline PathD MakePathZD(const T2(&list)[N])
{
static_assert(N % 3 == 0,
"MakePathZD requires values in multiples of 3");
std::size_t size = N / 3;
PathD result(size);
if constexpr (std::numeric_limits<T2>::is_integer)
for (size_t i = 0; i < size; ++i)
result[i] = PointD(list[i * 3],
list[i * 3 + 1], list[i * 3 + 2]);
else
for (size_t i = 0; i < size; ++i)
result[i] = PointD(list[i * 3], list[i * 3 + 1],
static_cast<int64_t>(list[i * 3 + 2]));
return result;
}
#endif
inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false)
{
size_t len = p.size();
if (len < 3)
{
if (!is_open_path || len < 2 || p[0] == p[1]) return Path64();
else return p;
}
Path64 dst;
dst.reserve(len);
Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1;
if (!is_open_path)
{
while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1)))
++srcIt;
while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt))
--stop;
if (srcIt == stop) return Path64();
}
prevIt = srcIt++;
dst.emplace_back(*prevIt);
for (; srcIt != stop; ++srcIt)
{
if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
{
prevIt = srcIt;
dst.emplace_back(*prevIt);
}
}
if (is_open_path)
dst.emplace_back(*srcIt);
else if (!IsCollinear(*prevIt, *stop, dst[0]))
dst.emplace_back(*stop);
else
{
while (dst.size() > 2 &&
IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
dst.pop_back();
if (dst.size() < 3) return Path64();
}
return dst;
}
inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
{
int error_code = 0;
CheckPrecisionRange(precision, error_code);
if (error_code) return PathD();
const double scale = std::pow(10, precision);
Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
if (error_code) return PathD();
p = TrimCollinear(p, is_open_path);
return ScalePath<double, int64_t>(p, 1/scale, error_code);
}
template <typename T>
inline double Distance(const Point<T> pt1, const Point<T> pt2)
{
return std::sqrt(DistanceSqr(pt1, pt2));
}
template <typename T>
inline double Length(const Path<T>& path, bool is_closed_path = false)
{
double result = 0.0;
if (path.size() < 2) return result;
auto it = path.cbegin(), stop = path.end() - 1;
for (; it != stop; ++it)
result += Distance(*it, *(it + 1));
if (is_closed_path)
result += Distance(*stop, *path.cbegin());
return result;
}
template <typename T>
inline bool NearCollinear(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3, double sin_sqrd_min_angle_rads)
{
double cp = std::abs(CrossProduct(pt1, pt2, pt3));
return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
}
template <typename T>
inline Path<T> Ellipse(const Rect<T>& rect, size_t steps = 0)
{
return Ellipse(rect.MidPoint(),
static_cast<double>(rect.Width()) *0.5,
static_cast<double>(rect.Height()) * 0.5, steps);
}
template <typename T>
inline Path<T> Ellipse(const Point<T>& center,
double radiusX, double radiusY = 0, size_t steps = 0)
{
if (radiusX <= 0) return Path<T>();
if (radiusY <= 0) radiusY = radiusX;
if (steps <= 2)
steps = static_cast<size_t>(PI * sqrt((radiusX + radiusY) / 2));
double si = std::sin(2 * PI / steps);
double co = std::cos(2 * PI / steps);
double dx = co, dy = si;
Path<T> result;
result.reserve(steps);
result.emplace_back(center.x + radiusX, static_cast<double>(center.y));
for (size_t i = 1; i < steps; ++i)
{
result.emplace_back(center.x + radiusX * dx, center.y + radiusY * dy);
double x = dx * co - dy * si;
dy = dy * co + dx * si;
dx = x;
}
return result;
}
inline size_t GetNext(size_t current, size_t high,
const std::vector<bool>& flags)
{
++current;
while (current <= high && flags[current]) ++current;
if (current <= high) return current;
current = 0;
while (flags[current]) ++current;
return current;
}
inline size_t GetPrior(size_t current, size_t high,
const std::vector<bool>& flags)
{
if (current == 0) current = high;
else --current;
while (current > 0 && flags[current]) --current;
if (!flags[current]) return current;
current = high;
while (flags[current]) --current;
return current;
}
template <typename T>
inline Path<T> SimplifyPath(const Path<T> &path,
double epsilon, bool isClosedPath = true)
{
const size_t len = path.size(), high = len -1;
const double epsSqr = Sqr(epsilon);
if (len < 4) return Path<T>(path);
std::vector<bool> flags(len);
std::vector<double> distSqr(len);
size_t prior = high, curr = 0, start, next, prior2;
if (isClosedPath)
{
distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
}
else
{
distSqr[0] = MAX_DBL;
distSqr[high] = MAX_DBL;
}
for (size_t i = 1; i < high; ++i)
distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]);
for (;;)
{
if (distSqr[curr] > epsSqr)
{
start = curr;
do
{
curr = GetNext(curr, high, flags);
} while (curr != start && distSqr[curr] > epsSqr);
if (curr == start) break;
}
prior = GetPrior(curr, high, flags);
next = GetNext(curr, high, flags);
if (next == prior) break;
// flag for removal the smaller of adjacent 'distances'
if (distSqr[next] < distSqr[curr])
{
prior2 = prior;
prior = curr;
curr = next;
next = GetNext(next, high, flags);
}
else
prior2 = GetPrior(prior, high, flags);
flags[curr] = true;
curr = next;
next = GetNext(next, high, flags);
if (isClosedPath || ((curr != high) && (curr != 0)))
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
if (isClosedPath || ((prior != 0) && (prior != high)))
distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]);
}
Path<T> result;
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (!flags[i]) result.emplace_back(path[i]);
return result;
}
template <typename T>
inline Paths<T> SimplifyPaths(const Paths<T> &paths,
double epsilon, bool isClosedPath = true)
{
Paths<T> result;
result.reserve(paths.size());
for (const auto& path : paths)
result.emplace_back(std::move(SimplifyPath(path, epsilon, isClosedPath)));
return result;
}
template <typename T>
inline void RDP(const Path<T> path, std::size_t begin,
std::size_t end, double epsSqrd, std::vector<bool>& flags)
{
typename Path<T>::size_type idx = 0;
double max_d = 0;
while (end > begin && path[begin] == path[end]) flags[end--] = false;
for (typename Path<T>::size_type i = begin + 1; i < end; ++i)
{
// PerpendicDistFromLineSqrd - avoids expensive Sqrt()
double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
if (d <= max_d) continue;
max_d = d;
idx = i;
}
if (max_d <= epsSqrd) return;
flags[idx] = true;
if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags);
if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags);
}
template <typename T>
inline Path<T> RamerDouglasPeucker(const Path<T>& path, double epsilon)
{
const typename Path<T>::size_type len = path.size();
if (len < 5) return Path<T>(path);
std::vector<bool> flags(len);
flags[0] = true;
flags[len - 1] = true;
RDP(path, 0, len - 1, Sqr(epsilon), flags);
Path<T> result;
result.reserve(len);
for (typename Path<T>::size_type i = 0; i < len; ++i)
if (flags[i])
result.emplace_back(path[i]);
return result;
}
template <typename T>
inline Paths<T> RamerDouglasPeucker(const Paths<T>& paths, double epsilon)
{
Paths<T> result;
result.reserve(paths.size());
std::transform(paths.begin(), paths.end(), back_inserter(result),
[epsilon](const auto& path)
{ return RamerDouglasPeucker<T>(path, epsilon); });
return result;
}
} // end Clipper2Lib namespace
#endif // CLIPPER_H

View File

@@ -0,0 +1,120 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 1 November 2023 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2023 *
* Purpose : Minkowski Sum and Difference *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_MINKOWSKI_H
#define CLIPPER_MINKOWSKI_H
#include "clipper2/clipper.core.h"
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
namespace detail
{
inline Paths64 Minkowski(const Path64& pattern, const Path64& path, bool isSum, bool isClosed)
{
size_t delta = isClosed ? 0 : 1;
size_t patLen = pattern.size(), pathLen = path.size();
if (patLen == 0 || pathLen == 0) return Paths64();
Paths64 tmp;
tmp.reserve(pathLen);
if (isSum)
{
for (const Point64& p : path)
{
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p + pt2; });
tmp.emplace_back(std::move(path2));
}
}
else
{
for (const Point64& p : path)
{
Path64 path2(pattern.size());
std::transform(pattern.cbegin(), pattern.cend(),
path2.begin(), [p](const Point64& pt2) {return p - pt2; });
tmp.emplace_back(std::move(path2));
}
}
Paths64 result;
result.reserve((pathLen - delta) * patLen);
size_t g = isClosed ? pathLen - 1 : 0;
for (size_t h = patLen - 1, i = delta; i < pathLen; ++i)
{
for (size_t j = 0; j < patLen; j++)
{
Path64 quad;
quad.reserve(4);
{
quad.emplace_back(tmp[g][h]);
quad.emplace_back(tmp[i][h]);
quad.emplace_back(tmp[i][j]);
quad.emplace_back(tmp[g][j]);
};
if (!IsPositive(quad))
std::reverse(quad.begin(), quad.end());
result.emplace_back(std::move(quad));
h = j;
}
g = i;
}
return result;
}
inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
{
Paths64 result;
Clipper64 clipper;
clipper.AddSubject(subjects);
clipper.Execute(ClipType::Union, fillrule, result);
return result;
}
} // namespace internal
inline Paths64 MinkowskiSum(const Path64& pattern, const Path64& path, bool isClosed)
{
return detail::Union(detail::Minkowski(pattern, path, true, isClosed), FillRule::NonZero);
}
inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{
int error_code = 0;
double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
}
inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed)
{
return detail::Union(detail::Minkowski(pattern, path, false, isClosed), FillRule::NonZero);
}
inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
{
int error_code = 0;
double scale = pow(10, decimalPlaces);
Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero);
return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
}
} // Clipper2Lib namespace
#endif // CLIPPER_MINKOWSKI_H

View File

@@ -0,0 +1,129 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_OFFSET_H_
#define CLIPPER_OFFSET_H_
#include "clipper.core.h"
#include "clipper.engine.h"
#include <optional>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
enum class JoinType { Square, Bevel, Round, Miter };
//Square : Joins are 'squared' at exactly the offset distance (more complex code)
//Bevel : Similar to Square, but the offset distance varies with angle (simple code & faster)
enum class EndType {Polygon, Joined, Butt, Square, Round};
//Butt : offsets both sides of a path, with square blunt ends
//Square : offsets both sides of a path, with square extended ends
//Round : offsets both sides of a path, with round extended ends
//Joined : offsets both sides of a path, with joined ends
//Polygon: offsets only one side of a closed path
typedef std::function<double(const Path64& path, const PathD& path_normals, size_t curr_idx, size_t prev_idx)> DeltaCallback64;
class ClipperOffset {
private:
class Group {
public:
Paths64 paths_in;
std::optional<size_t> lowest_path_idx{};
bool is_reversed = false;
JoinType join_type;
EndType end_type;
Group(const Paths64& _paths, JoinType _join_type, EndType _end_type);
};
int error_code_ = 0;
double delta_ = 0.0;
double group_delta_ = 0.0;
double temp_lim_ = 0.0;
double steps_per_rad_ = 0.0;
double step_sin_ = 0.0;
double step_cos_ = 0.0;
PathD norms;
Path64 path_out;
Paths64* solution = nullptr;
PolyTree64* solution_tree = nullptr;
std::vector<Group> groups_;
JoinType join_type_ = JoinType::Bevel;
EndType end_type_ = EndType::Polygon;
double miter_limit_ = 0.0;
double arc_tolerance_ = 0.0;
bool preserve_collinear_ = false;
bool reverse_solution_ = false;
#ifdef USINGZ
ZCallback64 zCallback64_ = nullptr;
void ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip);
#endif
DeltaCallback64 deltaCallback64_ = nullptr;
size_t CalcSolutionCapacity();
bool CheckReverseOrientation();
void DoBevel(const Path64& path, size_t j, size_t k);
void DoSquare(const Path64& path, size_t j, size_t k);
void DoMiter(const Path64& path, size_t j, size_t k, double cos_a);
void DoRound(const Path64& path, size_t j, size_t k, double angle);
void BuildNormals(const Path64& path);
void OffsetPolygon(Group& group, const Path64& path);
void OffsetOpenJoined(Group& group, const Path64& path);
void OffsetOpenPath(Group& group, const Path64& path);
void OffsetPoint(Group& group, const Path64& path, size_t j, size_t k);
void DoGroupOffset(Group &group);
void ExecuteInternal(double delta);
public:
explicit ClipperOffset(double miter_limit = 2.0,
double arc_tolerance = 0.0,
bool preserve_collinear = false,
bool reverse_solution = false) :
miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
preserve_collinear_(preserve_collinear),
reverse_solution_(reverse_solution) { };
~ClipperOffset() { Clear(); };
int ErrorCode() const { return error_code_; };
void AddPath(const Path64& path, JoinType jt_, EndType et_);
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); };
void Execute(double delta, Paths64& sols_64);
void Execute(double delta, PolyTree64& polytree);
void Execute(DeltaCallback64 delta_cb, Paths64& paths);
double MiterLimit() const { return miter_limit_; }
void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; }
//ArcTolerance: needed for rounded offsets (See offset_triginometry2.svg)
double ArcTolerance() const { return arc_tolerance_; }
void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; }
bool PreserveCollinear() const { return preserve_collinear_; }
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
bool ReverseSolution() const { return reverse_solution_; }
void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;}
#ifdef USINGZ
void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
#endif
void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; }
};
}
#endif /* CLIPPER_OFFSET_H_ */

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 5 July 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_RECTCLIP_H
#define CLIPPER_RECTCLIP_H
#include "clipper2/clipper.core.h"
#include <queue>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
// Location: the order is important here, see StartLocsIsClockwise()
enum class Location { Left, Top, Right, Bottom, Inside };
class OutPt2;
typedef std::vector<OutPt2*> OutPt2List;
class OutPt2 {
public:
Point64 pt;
size_t owner_idx = 0;
OutPt2List* edge = nullptr;
OutPt2* next = nullptr;
OutPt2* prev = nullptr;
};
//------------------------------------------------------------------------------
// RectClip64
//------------------------------------------------------------------------------
class RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
protected:
const Rect64 rect_;
const Path64 rect_as_path_;
const Point64 rect_mp_;
Rect64 path_bounds_;
std::deque<OutPt2> op_container_;
OutPt2List results_; // each path can be broken into multiples
OutPt2List edges_[8]; // clockwise and counter-clockwise
std::vector<Location> start_locs_;
void CheckEdges();
void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw);
void GetNextLocation(const Path64& path,
Location& loc, size_t& i, size_t highI);
OutPt2* Add(Point64 pt, bool start_new = false);
void AddCorner(Location prev, Location curr);
void AddCorner(Location& loc, bool isClockwise);
public:
explicit RectClip64(const Rect64& rect) :
rect_(rect),
rect_as_path_(rect.AsPath()),
rect_mp_(rect.MidPoint()) {}
Paths64 Execute(const Paths64& paths);
};
//------------------------------------------------------------------------------
// RectClipLines64
//------------------------------------------------------------------------------
class RectClipLines64 : public RectClip64 {
private:
void ExecuteInternal(const Path64& path);
Path64 GetPath(OutPt2*& op);
public:
explicit RectClipLines64(const Rect64& rect) : RectClip64(rect) {};
Paths64 Execute(const Paths64& paths);
};
} // Clipper2Lib namespace
#endif // CLIPPER_RECTCLIP_H

View File

@@ -0,0 +1,6 @@
#ifndef CLIPPER_VERSION_H
#define CLIPPER_VERSION_H
constexpr auto CLIPPER2_VERSION = "1.5.2";
#endif // CLIPPER_VERSION_H

View File

@@ -0,0 +1,17 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper2 library with the Z support.
#ifndef clipper2_z_hpp
#ifdef CLIPPER_H
#error "You should include clipper2_z.hpp before clipper.h"
#endif
#define clipper2_z_hpp
// Enable the Z coordinate support.
#define USINGZ
#include "clipper.h"
#undef CLIPPER_H
#undef USINGZ
#endif // clipper2_z_hpp

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,658 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 22 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#include "clipper2/clipper.h"
#include "clipper2/clipper.offset.h"
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
const double floating_point_tolerance = 1e-12;
// Clipper2 approximates arcs by using series of relatively short straight
//line segments. And logically, shorter line segments will produce better arc
// approximations. But very short segments can degrade performance, usually
// with little or no discernable improvement in curve quality. Very short
// segments can even detract from curve quality, due to the effects of integer
// rounding. Since there isn't an optimal number of line segments for any given
// arc radius (that perfectly balances curve approximation with performance),
// arc tolerance is user defined. Nevertheless, when the user doesn't define
// an arc tolerance (ie leaves alone the 0 default value), the calculated
// default arc tolerance (offset_radius / 500) generally produces good (smooth)
// arc approximations without producing excessively small segment lengths.
// See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm
const double arc_const = 0.002; // <-- 1/500
//------------------------------------------------------------------------------
// Miscellaneous methods
//------------------------------------------------------------------------------
std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
{
std::optional<size_t> result;
Point64 botPt = Point64(INT64_MAX, INT64_MIN);
for (size_t i = 0; i < paths.size(); ++i)
{
for (const Point64& pt : paths[i])
{
if ((pt.y < botPt.y) ||
((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
result = i;
botPt.x = pt.x;
botPt.y = pt.y;
}
}
return result;
}
inline double Hypot(double x, double y)
{
// given that this is an internal function, and given the x and y parameters
// will always be coordinate values (or the difference between coordinate values),
// x and y should always be within INT64_MIN to INT64_MAX. Consequently,
// there should be no risk that the following computation will overflow
// see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
}
static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2)
{
if (pt1 == pt2) return PointD(0.0, 0.0);
double dx = static_cast<double>(pt2.x - pt1.x);
double dy = static_cast<double>(pt2.y - pt1.y);
double inverse_hypot = 1.0 / Hypot(dx, dy);
dx *= inverse_hypot;
dy *= inverse_hypot;
return PointD(dy, -dx);
}
inline bool AlmostZero(double value, double epsilon = 0.001)
{
return std::fabs(value) < epsilon;
}
inline PointD NormalizeVector(const PointD& vec)
{
double h = Hypot(vec.x, vec.y);
if (AlmostZero(h)) return PointD(0,0);
double inverseHypot = 1 / h;
return PointD(vec.x * inverseHypot, vec.y * inverseHypot);
}
inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2)
{
return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y));
}
inline bool IsClosedPath(EndType et)
{
return et == EndType::Polygon || et == EndType::Joined;
}
static inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta)
{
#ifdef USINGZ
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
}
inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta)
{
#ifdef USINGZ
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z);
#else
return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta);
#endif
}
inline void NegatePath(PathD& path)
{
for (PointD& pt : path)
{
pt.x = -pt.x;
pt.y = -pt.y;
#ifdef USINGZ
pt.z = pt.z;
#endif
}
}
//------------------------------------------------------------------------------
// ClipperOffset::Group methods
//------------------------------------------------------------------------------
ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType _end_type):
paths_in(_paths), join_type(_join_type), end_type(_end_type)
{
bool is_joined =
(end_type == EndType::Polygon) ||
(end_type == EndType::Joined);
for (Path64& p: paths_in)
StripDuplicates(p, is_joined);
if (end_type == EndType::Polygon)
{
lowest_path_idx = GetLowestClosedPathIdx(paths_in);
// the lowermost path must be an outer path, so if its orientation is negative,
// then flag the whole group is 'reversed' (will negate delta etc.)
// as this is much more efficient than reversing every path.
is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
}
else
{
lowest_path_idx = std::nullopt;
is_reversed = false;
}
}
//------------------------------------------------------------------------------
// ClipperOffset methods
//------------------------------------------------------------------------------
void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_)
{
groups_.emplace_back(Paths64(1, path), jt_, et_);
}
void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_)
{
if (paths.size() == 0) return;
groups_.emplace_back(paths, jt_, et_);
}
void ClipperOffset::BuildNormals(const Path64& path)
{
norms.clear();
norms.reserve(path.size());
if (path.size() == 0) return;
Path64::const_iterator path_iter, path_stop_iter = --path.cend();
for (path_iter = path.cbegin(); path_iter != path_stop_iter; ++path_iter)
norms.emplace_back(GetUnitNormal(*path_iter,*(path_iter +1)));
norms.emplace_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
}
void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
{
PointD pt1, pt2;
if (j == k)
{
double abs_delta = std::abs(group_delta_);
#ifdef USINGZ
pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z);
pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z);
#else
pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
#endif
}
else
{
#ifdef USINGZ
pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z);
pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z);
#else
pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
#endif
}
path_out.emplace_back(pt1);
path_out.emplace_back(pt2);
}
void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
{
PointD vec;
if (j == k)
vec = PointD(norms[j].y, -norms[j].x);
else
vec = GetAvgUnitVector(
PointD(-norms[k].y, norms[k].x),
PointD(norms[j].y, -norms[j].x));
double abs_delta = std::abs(group_delta_);
// now offset the original vertex delta units along unit vector
PointD ptQ = PointD(path[j]);
ptQ = TranslatePoint(ptQ, abs_delta * vec.x, abs_delta * vec.y);
// get perpendicular vertices
PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x);
PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x);
// get 2 vertices along one edge offset
PointD pt3 = GetPerpendicD(path[k], norms[k], group_delta_);
if (j == k)
{
PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = ptQ;
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
//get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ));
path_out.emplace_back(pt);
}
else
{
PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = ptQ;
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
path_out.emplace_back(pt);
//get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ));
}
}
void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a)
{
double q = group_delta_ / (cos_a + 1);
#ifdef USINGZ
path_out.emplace_back(
path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q,
path[j].z);
#else
path_out.emplace_back(
path[j].x + (norms[k].x + norms[j].x) * q,
path[j].y + (norms[k].y + norms[j].y) * q);
#endif
}
void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle)
{
if (deltaCallback64_) {
// when deltaCallback64_ is assigned, group_delta_ won't be constant,
// so we'll need to do the following calculations for *every* vertex.
double abs_delta = std::fabs(group_delta_);
double arcTol = (arc_tolerance_ > floating_point_tolerance ?
std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const);
double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 * PI);
}
Point64 pt = path[j];
PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_);
if (j == k) offsetVec.Negate();
#ifdef USINGZ
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
#else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif
int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
for (int i = 1; i < steps; ++i) // ie 1 less than steps
{
offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
offsetVec.x * step_sin_ + offsetVec.y * step_cos_);
#ifdef USINGZ
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
#else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif
}
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
}
void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size_t k)
{
// Let A = change in angle where edges join
// A == 0: ie no change in angle (flat join)
// A == PI: edges 'spike'
// sin(A) < 0: right turning
// cos(A) < 0: change in angle is more than 90 degree
if (path[j] == path[k]) return;
double sin_a = CrossProduct(norms[j], norms[k]);
double cos_a = DotProduct(norms[j], norms[k]);
if (sin_a > 1.0) sin_a = 1.0;
else if (sin_a < -1.0) sin_a = -1.0;
if (deltaCallback64_) {
group_delta_ = deltaCallback64_(path, norms, j, k);
if (group.is_reversed) group_delta_ = -group_delta_;
}
if (std::fabs(group_delta_) <= floating_point_tolerance)
{
path_out.emplace_back(path[j]);
return;
}
if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
{
// is concave
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// to ensure that path reversals (ie over-shrunk paths) are removed.
#ifdef USINGZ
path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_), path[j].z);
path_out.emplace_back(path[j]); // (#405, #873, #916)
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_), path[j].z);
#else
path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_));
path_out.emplace_back(path[j]); // (#405, #873, #916)
path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
#endif
}
else if (cos_a > 0.999 && join_type_ != JoinType::Round)
{
// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
DoMiter(path, j, k, cos_a);
}
else if (join_type_ == JoinType::Miter)
{
// miter unless the angle is sufficiently acute to exceed ML
if (cos_a > temp_lim_ - 1) DoMiter(path, j, k, cos_a);
else DoSquare(path, j, k);
}
else if (join_type_ == JoinType::Round)
DoRound(path, j, k, std::atan2(sin_a, cos_a));
else if ( join_type_ == JoinType::Bevel)
DoBevel(path, j, k);
else
DoSquare(path, j, k);
}
void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
{
path_out.clear();
for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
OffsetPoint(group, path, j, k);
solution->emplace_back(path_out);
}
void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
{
OffsetPolygon(group, path);
Path64 reverse_path(path);
std::reverse(reverse_path.begin(), reverse_path.end());
//rebuild normals
std::reverse(norms.begin(), norms.end());
norms.emplace_back(norms[0]);
norms.erase(norms.begin());
NegatePath(norms);
OffsetPolygon(group, reverse_path);
}
void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
{
// do the line start cap
if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
if (std::fabs(group_delta_) <= floating_point_tolerance)
path_out.emplace_back(path[0]);
else
{
switch (end_type_)
{
case EndType::Butt:
DoBevel(path, 0, 0);
break;
case EndType::Round:
DoRound(path, 0, 0, PI);
break;
default:
DoSquare(path, 0, 0);
break;
}
}
size_t highI = path.size() - 1;
// offset the left side going forward
for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
OffsetPoint(group, path, j, k);
// reverse normals
for (size_t i = highI; i > 0; --i)
norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
norms[0] = norms[highI];
// do the line end cap
if (deltaCallback64_)
group_delta_ = deltaCallback64_(path, norms, highI, highI);
if (std::fabs(group_delta_) <= floating_point_tolerance)
path_out.emplace_back(path[highI]);
else
{
switch (end_type_)
{
case EndType::Butt:
DoBevel(path, highI, highI);
break;
case EndType::Round:
DoRound(path, highI, highI, PI);
break;
default:
DoSquare(path, highI, highI);
break;
}
}
for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
OffsetPoint(group, path, j, k);
solution->emplace_back(path_out);
}
void ClipperOffset::DoGroupOffset(Group& group)
{
if (group.end_type == EndType::Polygon)
{
// a straight path (2 points) can now also be 'polygon' offset
// where the ends will be treated as (180 deg.) joins
if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_);
group_delta_ = (group.is_reversed) ? -delta_ : delta_;
}
else
group_delta_ = std::abs(delta_);// *0.5;
double abs_delta = std::fabs(group_delta_);
join_type_ = group.join_type;
end_type_ = group.end_type;
if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
{
// calculate the number of steps required to approximate a circle
// (see https://www.angusj.com/clipper2/Docs/Trigonometry.htm)
// arcTol - when arc_tolerance_ is undefined (0) then curve imprecision
// will be relative to the size of the offset (delta). Obviously very
//large offsets will almost always require much less precision.
double arcTol = (arc_tolerance_ > floating_point_tolerance) ?
std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const;
double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI);
step_sin_ = std::sin(2 * PI / steps_per_360);
step_cos_ = std::cos(2 * PI / steps_per_360);
if (group_delta_ < 0.0) step_sin_ = -step_sin_;
steps_per_rad_ = steps_per_360 / (2 * PI);
}
//double min_area = PI * Sqr(group_delta_);
Paths64::const_iterator path_in_it = group.paths_in.cbegin();
for ( ; path_in_it != group.paths_in.cend(); ++path_in_it)
{
Path64::size_type pathLen = path_in_it->size();
path_out.clear();
if (pathLen == 1) // single point
{
if (deltaCallback64_)
{
group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0);
if (group.is_reversed) group_delta_ = -group_delta_;
abs_delta = std::fabs(group_delta_);
}
if (group_delta_ < 1) continue;
const Point64& pt = (*path_in_it)[0];
//single vertex so build a circle or square ...
if (group.join_type == JoinType::Round)
{
double radius = abs_delta;
size_t steps = steps_per_rad_ > 0 ? static_cast<size_t>(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617
path_out = Ellipse(pt, radius, radius, steps);
#ifdef USINGZ
for (auto& p : path_out) p.z = pt.z;
#endif
}
else
{
int d = (int)std::ceil(abs_delta);
Rect64 r = Rect64(pt.x - d, pt.y - d, pt.x + d, pt.y + d);
path_out = r.AsPath();
#ifdef USINGZ
for (auto& p : path_out) p.z = pt.z;
#endif
}
solution->emplace_back(path_out);
continue;
} // end of offsetting a single point
if ((pathLen == 2) && (group.end_type == EndType::Joined))
end_type_ = (group.join_type == JoinType::Round) ?
EndType::Round :
EndType::Square;
BuildNormals(*path_in_it);
if (end_type_ == EndType::Polygon) OffsetPolygon(group, *path_in_it);
else if (end_type_ == EndType::Joined) OffsetOpenJoined(group, *path_in_it);
else OffsetOpenPath(group, *path_in_it);
}
}
#ifdef USINGZ
void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1,
const Point64& bot2, const Point64& top2, Point64& ip)
{
if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z;
else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z;
else if (top1.z && (top1.z == top2.z)) ip.z = top1.z;
else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip);
}
#endif
size_t ClipperOffset::CalcSolutionCapacity()
{
size_t result = 0;
for (const Group& g : groups_)
result += (g.end_type == EndType::Joined) ? g.paths_in.size() * 2 : g.paths_in.size();
return result;
}
bool ClipperOffset::CheckReverseOrientation()
{
// nb: this assumes there's consistency in orientation between groups
bool is_reversed_orientation = false;
for (const Group& g : groups_)
if (g.end_type == EndType::Polygon)
{
is_reversed_orientation = g.is_reversed;
break;
}
return is_reversed_orientation;
}
void ClipperOffset::ExecuteInternal(double delta)
{
error_code_ = 0;
if (groups_.size() == 0) return;
solution->reserve(CalcSolutionCapacity());
if (std::abs(delta) < 0.5) // ie: offset is insignificant
{
Paths64::size_type sol_size = 0;
for (const Group& group : groups_) sol_size += group.paths_in.size();
solution->reserve(sol_size);
for (const Group& group : groups_)
copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution));
}
else
{
temp_lim_ = (miter_limit_ <= 1) ?
2.0 :
2.0 / (miter_limit_ * miter_limit_);
delta_ = delta;
std::vector<Group>::iterator git;
for (git = groups_.begin(); git != groups_.end(); ++git)
{
DoGroupOffset(*git);
if (!error_code_) continue; // all OK
solution->clear();
}
}
if (!solution->size()) return;
bool paths_reversed = CheckReverseOrientation();
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear(false);
//the solution should retain the orientation of the input
c.ReverseSolution(reverse_solution_ != paths_reversed);
#ifdef USINGZ
auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
c.SetZCallback(fp);
#endif
c.AddSubject(*solution);
if (solution_tree)
{
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, *solution_tree);
else
c.Execute(ClipType::Union, FillRule::Positive, *solution_tree);
}
else
{
if (paths_reversed)
c.Execute(ClipType::Union, FillRule::Negative, *solution);
else
c.Execute(ClipType::Union, FillRule::Positive, *solution);
}
}
void ClipperOffset::Execute(double delta, Paths64& paths64)
{
paths64.clear();
solution = &paths64;
solution_tree = nullptr;
ExecuteInternal(delta);
}
void ClipperOffset::Execute(double delta, PolyTree64& polytree)
{
polytree.Clear();
solution_tree = &polytree;
solution = new Paths64();
ExecuteInternal(delta);
delete solution;
solution = nullptr;
}
void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)
{
deltaCallback64_ = delta_cb;
Execute(1.0, paths);
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
// Enable the Z coordinate support.
#define USINGZ
// and let it compile
#include "clipper.engine.cpp"
#include "clipper.offset.cpp"
#include "clipper.rectclip.cpp"

View File

@@ -153,6 +153,8 @@ namespace ImGui
// const wchar_t CustomSupportsMarker = 0x1D;
// const wchar_t CustomSeamMarker = 0x1E;
// const wchar_t MmuSegmentationMarker = 0x1F;
const wchar_t HorizontalHide = 0xB1;
const wchar_t HorizontalShow = 0xB2;
// Do not forget use following letters only in wstring
//BBS use 08xx to avoid unicode character which may be used

View File

@@ -1,37 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(spline)
# Create interface library for spline (header-only library)
add_library(spline INTERFACE)
# Set include directories for the interface library
target_include_directories(spline SYSTEM
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
# Add compile features
target_compile_features(spline INTERFACE cxx_std_11)
# Create an alias for consistent naming
add_library(spline::spline ALIAS spline)
# Install headers if needed
install(FILES
spline.h
DESTINATION include/spline
)
# Install the interface library
install(TARGETS spline
EXPORT splineTargets
INCLUDES DESTINATION include
)
# Export the targets
install(EXPORT splineTargets
FILE splineTargets.cmake
NAMESPACE spline::
DESTINATION lib/cmake/spline
)

View File

@@ -1,944 +0,0 @@
/*
* spline.h
*
* simple cubic spline interpolation library without external
* dependencies
*
* ---------------------------------------------------------------------
* Copyright (C) 2011, 2014, 2016, 2021 Tino Kluge (ttk448 at gmail.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------
*
*/
#ifndef TK_SPLINE_H
#define TK_SPLINE_H
#include <cstdio>
#include <cassert>
#include <cmath>
#include <vector>
#include <algorithm>
#ifdef HAVE_SSTREAM
#include <sstream>
#include <string>
#endif // HAVE_SSTREAM
// not ideal but disable unused-function warnings
// (we get them because we have implementations in the header file,
// and this is because we want to be able to quickly separate them
// into a cpp file if necessary)
#if !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
namespace tk
{
// spline interpolation
class spline
{
public:
// spline types
enum spline_type {
linear = 10, // linear interpolation
cspline = 30, // cubic splines (classical C^2)
cspline_hermite = 31 // cubic hermite splines (local, only C^1)
};
// boundary condition type for the spline end-points
enum bd_type {
first_deriv = 1,
second_deriv = 2,
not_a_knot = 3
};
protected:
std::vector<double> m_x,m_y; // x,y coordinates of points
// interpolation parameters
// f(x) = a_i + b_i*(x-x_i) + c_i*(x-x_i)^2 + d_i*(x-x_i)^3
// where a_i = y_i, or else it won't go through grid points
std::vector<double> m_b,m_c,m_d; // spline coefficients
double m_c0; // for left extrapolation
spline_type m_type;
bd_type m_left, m_right;
double m_left_value, m_right_value;
bool m_made_monotonic;
void set_coeffs_from_b(); // calculate c_i, d_i from b_i
size_t find_closest(double x) const; // closest idx so that m_x[idx]<=x
public:
// default constructor: set boundary condition to be zero curvature
// at both ends, i.e. natural splines
spline(): m_type(cspline),
m_left(second_deriv), m_right(second_deriv),
m_left_value(0.0), m_right_value(0.0), m_made_monotonic(false)
{
;
}
spline(const std::vector<double>& X, const std::vector<double>& Y,
spline_type type = cspline,
bool make_monotonic = false,
bd_type left = second_deriv, double left_value = 0.0,
bd_type right = second_deriv, double right_value = 0.0
):
m_type(type),
m_left(left), m_right(right),
m_left_value(left_value), m_right_value(right_value),
m_made_monotonic(false) // false correct here: make_monotonic() sets it
{
this->set_points(X,Y,m_type);
if(make_monotonic) {
this->make_monotonic();
}
}
// modify boundary conditions: if called it must be before set_points()
void set_boundary(bd_type left, double left_value,
bd_type right, double right_value);
// set all data points (cubic_spline=false means linear interpolation)
void set_points(const std::vector<double>& x,
const std::vector<double>& y,
spline_type type=cspline);
// adjust coefficients so that the spline becomes piecewise monotonic
// where possible
// this is done by adjusting slopes at grid points by a non-negative
// factor and this will break C^2
// this can also break boundary conditions if adjustments need to
// be made at the boundary points
// returns false if no adjustments have been made, true otherwise
bool make_monotonic();
// evaluates the spline at point x
double operator() (double x) const;
double deriv(int order, double x) const;
// solves for all x so that: spline(x) = y
std::vector<double> solve(double y, bool ignore_extrapolation=true) const;
// returns the input data points
std::vector<double> get_x() const { return m_x; }
std::vector<double> get_y() const { return m_y; }
double get_x_min() const { assert(!m_x.empty()); return m_x.front(); }
double get_x_max() const { assert(!m_x.empty()); return m_x.back(); }
#ifdef HAVE_SSTREAM
// spline info string, i.e. spline type, boundary conditions etc.
std::string info() const;
#endif // HAVE_SSTREAM
};
namespace internal
{
// band matrix solver
class band_matrix
{
private:
std::vector< std::vector<double> > m_upper; // upper band
std::vector< std::vector<double> > m_lower; // lower band
public:
band_matrix() {}; // constructor
band_matrix(int dim, int n_u, int n_l); // constructor
~band_matrix() {}; // destructor
void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l
int dim() const; // matrix dimension
int num_upper() const
{
return (int)m_upper.size()-1;
}
int num_lower() const
{
return (int)m_lower.size()-1;
}
// access operator
double & operator () (int i, int j); // write
double operator () (int i, int j) const; // read
// we can store an additional diagonal (in m_lower)
double& saved_diag(int i);
double saved_diag(int i) const;
void lu_decompose();
std::vector<double> r_solve(const std::vector<double>& b) const;
std::vector<double> l_solve(const std::vector<double>& b) const;
std::vector<double> lu_solve(const std::vector<double>& b,
bool is_lu_decomposed=false);
};
double get_eps();
std::vector<double> solve_cubic(double a, double b, double c, double d,
int newton_iter=0);
} // namespace internal
// ---------------------------------------------------------------------
// implementation part, which could be separated into a cpp file
// ---------------------------------------------------------------------
// spline implementation
// -----------------------
void spline::set_boundary(spline::bd_type left, double left_value,
spline::bd_type right, double right_value)
{
assert(m_x.size()==0); // set_points() must not have happened yet
m_left=left;
m_right=right;
m_left_value=left_value;
m_right_value=right_value;
}
void spline::set_coeffs_from_b()
{
assert(m_x.size()==m_y.size());
assert(m_x.size()==m_b.size());
assert(m_x.size()>2);
size_t n=m_b.size();
if(m_c.size()!=n)
m_c.resize(n);
if(m_d.size()!=n)
m_d.resize(n);
for(size_t i=0; i<n-1; i++) {
const double h = m_x[i+1]-m_x[i];
// from continuity and differentiability condition
m_c[i] = ( 3.0*(m_y[i+1]-m_y[i])/h - (2.0*m_b[i]+m_b[i+1]) ) / h;
// from differentiability condition
m_d[i] = ( (m_b[i+1]-m_b[i])/(3.0*h) - 2.0/3.0*m_c[i] ) / h;
}
// for left extrapolation coefficients
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
}
void spline::set_points(const std::vector<double>& x,
const std::vector<double>& y,
spline_type type)
{
assert(x.size()==y.size());
assert(x.size()>=3);
// not-a-knot with 3 points has many solutions
if(m_left==not_a_knot || m_right==not_a_knot)
assert(x.size()>=4);
m_type=type;
m_made_monotonic=false;
m_x=x;
m_y=y;
int n = (int) x.size();
// check strict monotonicity of input vector x
for(int i=0; i<n-1; i++) {
assert(m_x[i]<m_x[i+1]);
}
if(type==linear) {
// linear interpolation
m_d.resize(n);
m_c.resize(n);
m_b.resize(n);
for(int i=0; i<n-1; i++) {
m_d[i]=0.0;
m_c[i]=0.0;
m_b[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
}
// ignore boundary conditions, set slope equal to the last segment
m_b[n-1]=m_b[n-2];
m_c[n-1]=0.0;
m_d[n-1]=0.0;
} else if(type==cspline) {
// classical cubic splines which are C^2 (twice cont differentiable)
// this requires solving an equation system
// setting up the matrix and right hand side of the equation system
// for the parameters b[]
int n_upper = (m_left == spline::not_a_knot) ? 2 : 1;
int n_lower = (m_right == spline::not_a_knot) ? 2 : 1;
internal::band_matrix A(n,n_upper,n_lower);
std::vector<double> rhs(n);
for(int i=1; i<n-1; i++) {
A(i,i-1)=1.0/3.0*(x[i]-x[i-1]);
A(i,i)=2.0/3.0*(x[i+1]-x[i-1]);
A(i,i+1)=1.0/3.0*(x[i+1]-x[i]);
rhs[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
}
// boundary conditions
if(m_left == spline::second_deriv) {
// 2*c[0] = f''
A(0,0)=2.0;
A(0,1)=0.0;
rhs[0]=m_left_value;
} else if(m_left == spline::first_deriv) {
// b[0] = f', needs to be re-expressed in terms of c:
// (2c[0]+c[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
A(0,0)=2.0*(x[1]-x[0]);
A(0,1)=1.0*(x[1]-x[0]);
rhs[0]=3.0*((y[1]-y[0])/(x[1]-x[0])-m_left_value);
} else if(m_left == spline::not_a_knot) {
// f'''(x[1]) exists, i.e. d[0]=d[1], or re-expressed in c:
// -h1*c[0] + (h0+h1)*c[1] - h0*c[2] = 0
A(0,0) = -(x[2]-x[1]);
A(0,1) = x[2]-x[0];
A(0,2) = -(x[1]-x[0]);
rhs[0] = 0.0;
} else {
assert(false);
}
if(m_right == spline::second_deriv) {
// 2*c[n-1] = f''
A(n-1,n-1)=2.0;
A(n-1,n-2)=0.0;
rhs[n-1]=m_right_value;
} else if(m_right == spline::first_deriv) {
// b[n-1] = f', needs to be re-expressed in terms of c:
// (c[n-2]+2c[n-1])(x[n-1]-x[n-2])
// = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
A(n-1,n-1)=2.0*(x[n-1]-x[n-2]);
A(n-1,n-2)=1.0*(x[n-1]-x[n-2]);
rhs[n-1]=3.0*(m_right_value-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
} else if(m_right == spline::not_a_knot) {
// f'''(x[n-2]) exists, i.e. d[n-3]=d[n-2], or re-expressed in c:
// -h_{n-2}*c[n-3] + (h_{n-3}+h_{n-2})*c[n-2] - h_{n-3}*c[n-1] = 0
A(n-1,n-3) = -(x[n-1]-x[n-2]);
A(n-1,n-2) = x[n-1]-x[n-3];
A(n-1,n-1) = -(x[n-2]-x[n-3]);
rhs[0] = 0.0;
} else {
assert(false);
}
// solve the equation system to obtain the parameters c[]
m_c=A.lu_solve(rhs);
// calculate parameters b[] and d[] based on c[]
m_d.resize(n);
m_b.resize(n);
for(int i=0; i<n-1; i++) {
m_d[i]=1.0/3.0*(m_c[i+1]-m_c[i])/(x[i+1]-x[i]);
m_b[i]=(y[i+1]-y[i])/(x[i+1]-x[i])
- 1.0/3.0*(2.0*m_c[i]+m_c[i+1])*(x[i+1]-x[i]);
}
// for the right extrapolation coefficients (zero cubic term)
// f_{n-1}(x) = y_{n-1} + b*(x-x_{n-1}) + c*(x-x_{n-1})^2
double h=x[n-1]-x[n-2];
// m_c[n-1] is determined by the boundary condition
m_d[n-1]=0.0;
m_b[n-1]=3.0*m_d[n-2]*h*h+2.0*m_c[n-2]*h+m_b[n-2]; // = f'_{n-2}(x_{n-1})
if(m_right==first_deriv)
m_c[n-1]=0.0; // force linear extrapolation
} else if(type==cspline_hermite) {
// hermite cubic splines which are C^1 (cont. differentiable)
// and derivatives are specified on each grid point
// (here we use 3-point finite differences)
m_b.resize(n);
m_c.resize(n);
m_d.resize(n);
// set b to match 1st order derivative finite difference
for(int i=1; i<n-1; i++) {
const double h = m_x[i+1]-m_x[i];
const double hl = m_x[i]-m_x[i-1];
m_b[i] = -h/(hl*(hl+h))*m_y[i-1] + (h-hl)/(hl*h)*m_y[i]
+ hl/(h*(hl+h))*m_y[i+1];
}
// boundary conditions determine b[0] and b[n-1]
if(m_left==first_deriv) {
m_b[0]=m_left_value;
} else if(m_left==second_deriv) {
const double h = m_x[1]-m_x[0];
m_b[0]=0.5*(-m_b[1]-0.5*m_left_value*h+3.0*(m_y[1]-m_y[0])/h);
} else if(m_left == not_a_knot) {
// f''' continuous at x[1]
const double h0 = m_x[1]-m_x[0];
const double h1 = m_x[2]-m_x[1];
m_b[0]= -m_b[1] + 2.0*(m_y[1]-m_y[0])/h0
+ h0*h0/(h1*h1)*(m_b[1]+m_b[2]-2.0*(m_y[2]-m_y[1])/h1);
} else {
assert(false);
}
if(m_right==first_deriv) {
m_b[n-1]=m_right_value;
m_c[n-1]=0.0;
} else if(m_right==second_deriv) {
const double h = m_x[n-1]-m_x[n-2];
m_b[n-1]=0.5*(-m_b[n-2]+0.5*m_right_value*h+3.0*(m_y[n-1]-m_y[n-2])/h);
m_c[n-1]=0.5*m_right_value;
} else if(m_right == not_a_knot) {
// f''' continuous at x[n-2]
const double h0 = m_x[n-2]-m_x[n-3];
const double h1 = m_x[n-1]-m_x[n-2];
m_b[n-1]= -m_b[n-2] + 2.0*(m_y[n-1]-m_y[n-2])/h1 + h1*h1/(h0*h0)
*(m_b[n-3]+m_b[n-2]-2.0*(m_y[n-2]-m_y[n-3])/h0);
// f'' continuous at x[n-1]: c[n-1] = 3*d[n-2]*h[n-2] + c[n-1]
m_c[n-1]=(m_b[n-2]+2.0*m_b[n-1])/h1-3.0*(m_y[n-1]-m_y[n-2])/(h1*h1);
} else {
assert(false);
}
m_d[n-1]=0.0;
// parameters c and d are determined by continuity and differentiability
set_coeffs_from_b();
} else {
assert(false);
}
// for left extrapolation coefficients
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
}
bool spline::make_monotonic()
{
assert(m_x.size()==m_y.size());
assert(m_x.size()==m_b.size());
assert(m_x.size()>2);
bool modified = false;
const int n=(int)m_x.size();
// make sure: input data monotonic increasing --> b_i>=0
// input data monotonic decreasing --> b_i<=0
for(int i=0; i<n; i++) {
int im1 = std::max(i-1, 0);
int ip1 = std::min(i+1, n-1);
if( ((m_y[im1]<=m_y[i]) && (m_y[i]<=m_y[ip1]) && m_b[i]<0.0) ||
((m_y[im1]>=m_y[i]) && (m_y[i]>=m_y[ip1]) && m_b[i]>0.0) ) {
modified=true;
m_b[i]=0.0;
}
}
// if input data is monotonic (b[i], b[i+1], avg have all the same sign)
// ensure a sufficient criteria for monotonicity is satisfied:
// sqrt(b[i]^2+b[i+1]^2) <= 3 |avg|, with avg=(y[i+1]-y[i])/h,
for(int i=0; i<n-1; i++) {
double h = m_x[i+1]-m_x[i];
double avg = (m_y[i+1]-m_y[i])/h;
if( avg==0.0 && (m_b[i]!=0.0 || m_b[i+1]!=0.0) ) {
modified=true;
m_b[i]=0.0;
m_b[i+1]=0.0;
} else if( (m_b[i]>=0.0 && m_b[i+1]>=0.0 && avg>0.0) ||
(m_b[i]<=0.0 && m_b[i+1]<=0.0 && avg<0.0) ) {
// input data is monotonic
double r = sqrt(m_b[i]*m_b[i]+m_b[i+1]*m_b[i+1])/std::fabs(avg);
if(r>3.0) {
// sufficient criteria for monotonicity: r<=3
// adjust b[i] and b[i+1]
modified=true;
m_b[i] *= (3.0/r);
m_b[i+1] *= (3.0/r);
}
}
}
if(modified==true) {
set_coeffs_from_b();
m_made_monotonic=true;
}
return modified;
}
// return the closest idx so that m_x[idx] <= x (return 0 if x<m_x[0])
size_t spline::find_closest(double x) const
{
std::vector<double>::const_iterator it;
it=std::upper_bound(m_x.begin(),m_x.end(),x); // *it > x
size_t idx = std::max( int(it-m_x.begin())-1, 0); // m_x[idx] <= x
return idx;
}
double spline::operator() (double x) const
{
// polynomial evaluation using Horner's scheme
// TODO: consider more numerically accurate algorithms, e.g.:
// - Clenshaw
// - Even-Odd method by A.C.R. Newbery
// - Compensated Horner Scheme
size_t n=m_x.size();
size_t idx=find_closest(x);
double h=x-m_x[idx];
double interpol;
if(x<m_x[0]) {
// extrapolation to the left
interpol=(m_c0*h + m_b[0])*h + m_y[0];
} else if(x>m_x[n-1]) {
// extrapolation to the right
interpol=(m_c[n-1]*h + m_b[n-1])*h + m_y[n-1];
} else {
// interpolation
interpol=((m_d[idx]*h + m_c[idx])*h + m_b[idx])*h + m_y[idx];
}
return interpol;
}
double spline::deriv(int order, double x) const
{
assert(order>0);
size_t n=m_x.size();
size_t idx = find_closest(x);
double h=x-m_x[idx];
double interpol;
if(x<m_x[0]) {
// extrapolation to the left
switch(order) {
case 1:
interpol=2.0*m_c0*h + m_b[0];
break;
case 2:
interpol=2.0*m_c0;
break;
default:
interpol=0.0;
break;
}
} else if(x>m_x[n-1]) {
// extrapolation to the right
switch(order) {
case 1:
interpol=2.0*m_c[n-1]*h + m_b[n-1];
break;
case 2:
interpol=2.0*m_c[n-1];
break;
default:
interpol=0.0;
break;
}
} else {
// interpolation
switch(order) {
case 1:
interpol=(3.0*m_d[idx]*h + 2.0*m_c[idx])*h + m_b[idx];
break;
case 2:
interpol=6.0*m_d[idx]*h + 2.0*m_c[idx];
break;
case 3:
interpol=6.0*m_d[idx];
break;
default:
interpol=0.0;
break;
}
}
return interpol;
}
std::vector<double> spline::solve(double y, bool ignore_extrapolation) const
{
std::vector<double> x; // roots for the entire spline
std::vector<double> root; // roots for each piecewise cubic
const size_t n=m_x.size();
// left extrapolation
if(ignore_extrapolation==false) {
root = internal::solve_cubic(m_y[0]-y,m_b[0],m_c0,0.0,1);
for(size_t j=0; j<root.size(); j++) {
if(root[j]<0.0) {
x.push_back(m_x[0]+root[j]);
}
}
}
// brute force check if piecewise cubic has roots in their resp. segment
// TODO: make more efficient
for(size_t i=0; i<n-1; i++) {
root = internal::solve_cubic(m_y[i]-y,m_b[i],m_c[i],m_d[i],1);
for(size_t j=0; j<root.size(); j++) {
double h = (i>0) ? (m_x[i]-m_x[i-1]) : 0.0;
double eps = internal::get_eps()*512.0*std::min(h,1.0);
if( (-eps<=root[j]) && (root[j]<m_x[i+1]-m_x[i]) ) {
double new_root = m_x[i]+root[j];
if(x.size()>0 && x.back()+eps > new_root) {
x.back()=new_root; // avoid spurious duplicate roots
} else {
x.push_back(new_root);
}
}
}
}
// right extrapolation
if(ignore_extrapolation==false) {
root = internal::solve_cubic(m_y[n-1]-y,m_b[n-1],m_c[n-1],0.0,1);
for(size_t j=0; j<root.size(); j++) {
if(0.0<=root[j]) {
x.push_back(m_x[n-1]+root[j]);
}
}
}
return x;
};
#ifdef HAVE_SSTREAM
std::string spline::info() const
{
std::stringstream ss;
ss << "type " << m_type << ", left boundary deriv " << m_left << " = ";
ss << m_left_value << ", right boundary deriv " << m_right << " = ";
ss << m_right_value << std::endl;
if(m_made_monotonic) {
ss << "(spline has been adjusted for piece-wise monotonicity)";
}
return ss.str();
}
#endif // HAVE_SSTREAM
namespace internal
{
// band_matrix implementation
// -------------------------
band_matrix::band_matrix(int dim, int n_u, int n_l)
{
resize(dim, n_u, n_l);
}
void band_matrix::resize(int dim, int n_u, int n_l)
{
assert(dim>0);
assert(n_u>=0);
assert(n_l>=0);
m_upper.resize(n_u+1);
m_lower.resize(n_l+1);
for(size_t i=0; i<m_upper.size(); i++) {
m_upper[i].resize(dim);
}
for(size_t i=0; i<m_lower.size(); i++) {
m_lower[i].resize(dim);
}
}
int band_matrix::dim() const
{
if(m_upper.size()>0) {
return (int)m_upper[0].size();
} else {
return 0;
}
}
// defines the new operator (), so that we can access the elements
// by A(i,j), index going from i=0,...,dim()-1
double & band_matrix::operator () (int i, int j)
{
int k=j-i; // what band is the entry
assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
assert( (-num_lower()<=k) && (k<=num_upper()) );
// k=0 -> diagonal, k<0 lower left part, k>0 upper right part
if(k>=0) return m_upper[k][i];
else return m_lower[-k][i];
}
double band_matrix::operator () (int i, int j) const
{
int k=j-i; // what band is the entry
assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
assert( (-num_lower()<=k) && (k<=num_upper()) );
// k=0 -> diagonal, k<0 lower left part, k>0 upper right part
if(k>=0) return m_upper[k][i];
else return m_lower[-k][i];
}
// second diag (used in LU decomposition), saved in m_lower
double band_matrix::saved_diag(int i) const
{
assert( (i>=0) && (i<dim()) );
return m_lower[0][i];
}
double & band_matrix::saved_diag(int i)
{
assert( (i>=0) && (i<dim()) );
return m_lower[0][i];
}
// LR-Decomposition of a band matrix
void band_matrix::lu_decompose()
{
int i_max,j_max;
int j_min;
double x;
// preconditioning
// normalize column i so that a_ii=1
for(int i=0; i<this->dim(); i++) {
assert(this->operator()(i,i)!=0.0);
this->saved_diag(i)=1.0/this->operator()(i,i);
j_min=std::max(0,i-this->num_lower());
j_max=std::min(this->dim()-1,i+this->num_upper());
for(int j=j_min; j<=j_max; j++) {
this->operator()(i,j) *= this->saved_diag(i);
}
this->operator()(i,i)=1.0; // prevents rounding errors
}
// Gauss LR-Decomposition
for(int k=0; k<this->dim(); k++) {
i_max=std::min(this->dim()-1,k+this->num_lower()); // num_lower not a mistake!
for(int i=k+1; i<=i_max; i++) {
assert(this->operator()(k,k)!=0.0);
x=-this->operator()(i,k)/this->operator()(k,k);
this->operator()(i,k)=-x; // assembly part of L
j_max=std::min(this->dim()-1,k+this->num_upper());
for(int j=k+1; j<=j_max; j++) {
// assembly part of R
this->operator()(i,j)=this->operator()(i,j)+x*this->operator()(k,j);
}
}
}
}
// solves Ly=b
std::vector<double> band_matrix::l_solve(const std::vector<double>& b) const
{
assert( this->dim()==(int)b.size() );
std::vector<double> x(this->dim());
int j_start;
double sum;
for(int i=0; i<this->dim(); i++) {
sum=0;
j_start=std::max(0,i-this->num_lower());
for(int j=j_start; j<i; j++) sum += this->operator()(i,j)*x[j];
x[i]=(b[i]*this->saved_diag(i)) - sum;
}
return x;
}
// solves Rx=y
std::vector<double> band_matrix::r_solve(const std::vector<double>& b) const
{
assert( this->dim()==(int)b.size() );
std::vector<double> x(this->dim());
int j_stop;
double sum;
for(int i=this->dim()-1; i>=0; i--) {
sum=0;
j_stop=std::min(this->dim()-1,i+this->num_upper());
for(int j=i+1; j<=j_stop; j++) sum += this->operator()(i,j)*x[j];
x[i]=( b[i] - sum ) / this->operator()(i,i);
}
return x;
}
std::vector<double> band_matrix::lu_solve(const std::vector<double>& b,
bool is_lu_decomposed)
{
assert( this->dim()==(int)b.size() );
std::vector<double> x,y;
if(is_lu_decomposed==false) {
this->lu_decompose();
}
y=this->l_solve(b);
x=this->r_solve(y);
return x;
}
// machine precision of a double, i.e. the successor of 1 is 1+eps
double get_eps()
{
//return std::numeric_limits<double>::epsilon(); // __DBL_EPSILON__
return 2.2204460492503131e-16; // 2^-52
}
// solutions for a + b*x = 0
std::vector<double> solve_linear(double a, double b)
{
std::vector<double> x; // roots
if(b==0.0) {
if(a==0.0) {
// 0*x = 0
x.resize(1);
x[0] = 0.0; // any x solves it but we need to pick one
return x;
} else {
// 0*x + ... = 0, no solution
return x;
}
} else {
x.resize(1);
x[0] = -a/b;
return x;
}
}
// solutions for a + b*x + c*x^2 = 0
std::vector<double> solve_quadratic(double a, double b, double c,
int newton_iter=0)
{
if(c==0.0) {
return solve_linear(a,b);
}
// rescale so that we solve x^2 + 2p x + q = (x+p)^2 + q - p^2 = 0
double p=0.5*b/c;
double q=a/c;
double discr = p*p-q;
const double eps=0.5*internal::get_eps();
double discr_err = (6.0*(p*p)+3.0*fabs(q)+fabs(discr))*eps;
std::vector<double> x; // roots
if(fabs(discr)<=discr_err) {
// discriminant is zero --> one root
x.resize(1);
x[0] = -p;
} else if(discr<0) {
// no root
} else {
// two roots
x.resize(2);
x[0] = -p - sqrt(discr);
x[1] = -p + sqrt(discr);
}
// improve solution via newton steps
for(size_t i=0; i<x.size(); i++) {
for(int k=0; k<newton_iter; k++) {
double f = (c*x[i] + b)*x[i] + a;
double f1 = 2.0*c*x[i] + b;
// only adjust if slope is large enough
if(fabs(f1)>1e-8) {
x[i] -= f/f1;
}
}
}
return x;
}
// solutions for the cubic equation: a + b*x +c*x^2 + d*x^3 = 0
// this is a naive implementation of the analytic solution without
// optimisation for speed or numerical accuracy
// newton_iter: number of newton iterations to improve analytical solution
// see also
// gsl: gsl_poly_solve_cubic() in solve_cubic.c
// octave: roots.m - via eigenvalues of the Frobenius companion matrix
std::vector<double> solve_cubic(double a, double b, double c, double d,
int newton_iter)
{
if(d==0.0) {
return solve_quadratic(a,b,c,newton_iter);
}
// convert to normalised form: a + bx + cx^2 + x^3 = 0
if(d!=1.0) {
a/=d;
b/=d;
c/=d;
}
// convert to depressed cubic: z^3 - 3pz - 2q = 0
// via substitution: z = x + c/3
std::vector<double> z; // roots of the depressed cubic
double p = -(1.0/3.0)*b + (1.0/9.0)*(c*c);
double r = 2.0*(c*c)-9.0*b;
double q = -0.5*a - (1.0/54.0)*(c*r);
double discr=p*p*p-q*q; // discriminant
// calculating numerical round-off errors with assumptions:
// - each operation is precise but each intermediate result x
// when stored has max error of x*eps
// - only multiplication with a power of 2 introduces no new error
// - a,b,c,d and some fractions (e.g. 1/3) have rounding errors eps
// - p_err << |p|, q_err << |q|, ... (this is violated in rare cases)
// would be more elegant to use boost::numeric::interval<double>
const double eps = internal::get_eps();
double p_err = eps*((3.0/3.0)*fabs(b)+(4.0/9.0)*(c*c)+fabs(p));
double r_err = eps*(6.0*(c*c)+18.0*fabs(b)+fabs(r));
double q_err = 0.5*fabs(a)*eps + (1.0/54.0)*fabs(c)*(r_err+fabs(r)*3.0*eps)
+ fabs(q)*eps;
double discr_err = (p*p) * (3.0*p_err + fabs(p)*2.0*eps)
+ fabs(q) * (2.0*q_err + fabs(q)*eps) + fabs(discr)*eps;
// depending on the discriminant we get different solutions
if(fabs(discr)<=discr_err) {
// discriminant zero: one or two real roots
if(fabs(p)<=p_err) {
// p and q are zero: single root
z.resize(1);
z[0] = 0.0; // triple root
} else {
z.resize(2);
z[0] = 2.0*q/p; // single root
z[1] = -0.5*z[0]; // double root
}
} else if(discr>0) {
// three real roots: via trigonometric solution
z.resize(3);
double ac = (1.0/3.0) * acos( q/(p*sqrt(p)) );
double sq = 2.0*sqrt(p);
z[0] = sq * cos(ac);
z[1] = sq * cos(ac-2.0*M_PI/3.0);
z[2] = sq * cos(ac-4.0*M_PI/3.0);
} else if (discr<0.0) {
// single real root: via Cardano's fromula
z.resize(1);
double sgnq = (q >= 0 ? 1 : -1);
double basis = fabs(q) + sqrt(-discr);
double C = sgnq * pow(basis, 1.0/3.0); // c++11 has std::cbrt()
z[0] = C + p/C;
}
for(size_t i=0; i<z.size(); i++) {
// convert depressed cubic roots to original cubic: x = z - c/3
z[i] -= (1.0/3.0)*c;
// improve solution via newton steps
for(int k=0; k<newton_iter; k++) {
double f = ((z[i] + c)*z[i] + b)*z[i] + a;
double f1 = (3.0*z[i] + 2.0*c)*z[i] + b;
// only adjust if slope is large enough
if(fabs(f1)>1e-8) {
z[i] -= f/f1;
}
}
}
// ensure if a=0 we get exactly x=0 as root
// TODO: remove this fudge
if(a==0.0) {
assert(z.size()>0); // cubic should always have at least one root
double xmin=fabs(z[0]);
size_t imin=0;
for(size_t i=1; i<z.size(); i++) {
if(xmin>fabs(z[i])) {
xmin=fabs(z[i]);
imin=i;
}
}
z[imin]=0.0; // replace the smallest absolute value with 0
}
std::sort(z.begin(), z.end());
return z;
}
} // namespace internal
} // namespace tk
#if !defined(_MSC_VER)
#pragma GCC diagnostic pop
#endif
#endif /* TK_SPLINE_H */

View File

@@ -1,161 +0,0 @@
# Welcome to the OrcaSlicer WIKI!
OrcaSlicer is a powerful open source slicer for FFF (FDM) 3D Printers. This wiki page aims to provide an detailed explanation of the slicer settings, how to get the most out of them as well as how to calibrate and setup your printer.
- [Printer Settings](#printer-settings)
- [Material Settings](#material-settings)
- [Process Settings](#process-settings)
- [Quality Settings](#quality-settings)
- [Strength Settings](#strength-settings)
- [Speed Settings](#speed-settings)
- [Support Settings](#support-settings)
- [Multimaterial Settings](#multimaterial-settings)
- [Others Settings](#others-settings)
- [Prepare](#prepare)
- [Calibrations](#calibrations)
- [Developer Section](#developer-section)
> [!WARNING]
> This wiki is community-maintained.
> Some pages may be **outdated** while others may be **newer** and present only in [nightly build](https://github.com/SoftFever/OrcaSlicer/releases/tag/nightly-builds) or [latest release](https://github.com/SoftFever/OrcaSlicer/releases).
> [!NOTE]
> Please consider contributing to the wiki following the [How to contribute to the wiki](How-to-wiki) guide.
## Printer Settings
![printer-preset](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/printer-preset.png?raw=true)
![printer](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/printer.svg?raw=true) Settings related to the 3D printer hardware and its configuration.
- [Air filtration/Exhaust fan handling](air-filtration)
- [Auxiliary fan handling](Auxiliary-fan)
- [Chamber temperature control](chamber-temperature)
- [Adaptive Bed Mesh](adaptive-bed-mesh)
- [Using different bed types in Orca](bed-types)
## Material Settings
![filament-preset](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/filament-preset.png?raw=true)
![filament](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/filament.svg?raw=true) Settings related to the 3D printing material.
- [Single Extruder Multimaterial](semm)
- [Pellet Printers (pellet flow coefficient)](pellet-flow-coefficient)
## Process Settings
![process-preset](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process-preset.png?raw=true)
![process](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/process.svg?raw=true) Settings related to the 3D printing process.
### Quality Settings
![custom-gcode_quality](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_quality.svg?raw=true) Settings related to print quality and aesthetics.
![process-quality](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-quality.png?raw=true)
- ![param_layer_height](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_layer_height.svg?raw=true) [Layer Height Settings](quality_settings_layer_height)
- ![param_line_width](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_line_width.svg?raw=true) [Line Width Settings](quality_settings_line_width)
- ![param_seam](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_seam.svg?raw=true) [Seam Settings](quality_settings_seam)
- ![param_precision](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_precision.svg?raw=true) [Precision](quality_settings_precision)
- ![param_ironing](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_ironing.svg?raw=true) [Ironing](quality_settings_ironing)
- ![param_wall_generator](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_wall_generator.svg?raw=true) [Wall generator](quality_settings_wall_generator)
- ![param_wall_surface](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_wall_surface.svg?raw=true) [Walls and surfaces](quality_settings_wall_and_surfaces)
- ![param_bridge](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_bridge.svg?raw=true) [Bridging](quality_settings_bridging)
- ![param_overhang](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_overhang.svg?raw=true) [Overhangs](quality_settings_overhangs)
### Strength Settings
![custom-gcode_strength](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_strength.svg?raw=true) Settings related to print strength and durability.
![process-strength](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-strength.png?raw=true)
- ![param_wall](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_wall.svg?raw=true) [Walls](strength_settings_walls)
- ![param_shell](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_shell.svg?raw=true) [Top and Bottom Shells](strength_settings_top_bottom_shells)
- ![param_infill](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_infill.svg?raw=true) [Infill](strength_settings_infill)
- ![param_concentric](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_concentric.svg?raw=true) [Fill Patterns](strength_settings_patterns)
- ![param_gcode](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_gcode.svg?raw=true) [Template Metalanguage for infill rotation](strength_settings_infill_rotation_template_metalanguage)
- ![param_advanced](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_advanced.svg?raw=true) [Advanced](strength_settings_advanced)
### Speed Settings
![custom-gcode_speed](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_speed.svg?raw=true) Settings related to print speed and movement.
![process-speed](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-speed.png?raw=true)
- ![param_speed_first](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_speed_first.svg?raw=true) [Initial Layer Speed](speed_settings_initial_layer_speed)
- ![param_speed](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_speed.svg?raw=true) [Other Layers Speed](speed_settings_other_layers_speed)
- ![param_overhang_speed](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_overhang_speed.svg?raw=true) [Overhang Speed](speed_settings_overhang_speed)
- ![param_travel_speed](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_travel_speed.svg?raw=true) [Travel Speed](speed_settings_travel)
- ![param_acceleration](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_acceleration.svg?raw=true) [Acceleration](speed_settings_acceleration)
- ![param_jerk](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_jerk.svg?raw=true) [Jerk (XY)](speed_settings_jerk_xy)
- ![param_advanced](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_advanced.svg?raw=true) [Advanced / Extrusion rate smoothing](speed_settings_advanced)
### Support Settings
![custom-gcode_support](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_support.svg?raw=true) Settings related to support structures and their properties.
![process-support](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-support.png?raw=true)
- ![param_support](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_support.svg?raw=true) [Support](support_settings_support)
- ![param_raft](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_raft.svg?raw=true) [Raft](support_settings_raft)
- ![param_support_filament](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_support_filament.svg?raw=true) [Support Filament](support_settings_filament)
- ![param_ironing](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_ironing.svg?raw=true) [Support Ironing](support_settings_ironing)
- ![param_advanced](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_advanced.svg?raw=true) [Advanced](support_settings_advanced)
- ![param_support_tree](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_support_tree.svg?raw=true) [Tree Supports](support_settings_tree)
### Multimaterial Settings
![custom-gcode_multi_material](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_multi_material.svg?raw=true) Settings related to multimaterial printing.
![process-multimaterial](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-multimaterial.png?raw=true)
- ![param_tower](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_tower.svg?raw=true) [Prime Tower](multimaterial_settings_prime_tower)
- ![param_filament_for_features](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_filament_for_features.svg?raw=true) [Filament for Features](multimaterial_settings_filament_for_features)
- ![param_ooze_prevention](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_ooze_prevention.svg?raw=true) [Ooze Prevention](multimaterial_settings_ooze_prevention)
- ![param_flush](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_flush.svg?raw=true) [Flush Options](multimaterial_settings_flush_options)
- ![param_advanced](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_advanced.svg?raw=true) [Advanced](multimaterial_settings_advanced)
### Others Settings
![custom-gcode_other](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/custom-gcode_other.svg?raw=true) Settings related to various other print settings.
![process-others](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process/process-others.png?raw=true)
- ![param_skirt](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_skirt.svg?raw=true) [Skirt](others_settings_skirt)
- ![param_adhension](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_adhension.svg?raw=true) [Brim](others_settings_brim)
- ![param_special](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_special.svg?raw=true) [Special Mode](others_settings_special_mode)
- ![fuzzy_skin](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/fuzzy_skin.svg?raw=true) [Fuzzy Skin](others_settings_fuzzy_skin)
- ![param_gcode](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_gcode.svg?raw=true) [G-Code Output](others_settings_g_code_output)
- ![param_gcode](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_gcode.svg?raw=true) [Post Processing Scripts](others_settings_post_processing_scripts)
- ![note](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/note.svg?raw=true) [Notes](others_settings_notes)
## Prepare
![tab_3d_active](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/tab_3d_active.svg?raw=true) First steps to prepare your model/s for printing.
- [STL Transformation](stl-transformation)
## Calibrations
![tab_calibration_active](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/tab_calibration_active.svg?raw=true) The [Calibration Guide](Calibration) outlines Orcas key calibration tests and their suggested order of execution.
- [Temperature](temp-calib)
- [Flow Rate](flow-rate-calib)
- [Pressure Advance](pressure-advance-calib)
- [Adaptive Pressure Advance Guide](adaptive-pressure-advance-calib)
- [Retraction](retraction-calib)
- [Tolerance](tolerance-calib)
- Advanced:
- [Volumetric Speed](volumetric-speed-calib)
- [Cornering (Jerk & Junction Deviation)](cornering-calib)
- [Input Shaping](input-shaping-calib)
- [VFA](vfa-calib)
## Developer Section
![im_code](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/im_code.svg?raw=true) This is a documentation from someone exploring the code and is by no means complete or even completely accurate. Please edit the parts you might find inaccurate. This is probably going to be helpful nonetheless.
- [How to build OrcaSlicer](How-to-build)
- [How to run tests](How-to-test)
- [Localization and translation guide](Localization_guide)
- [How to create profiles](How-to-create-profiles)
- [How to contribute to the wiki](How-to-wiki)
- [Preset, PresetBundle and PresetCollection](Preset-and-bundle)
- [Plater, Sidebar, Tab, ComboBox](plater-sidebar-tab-combobox)
- [Slicing Call Hierarchy](slicing-hierarchy)

View File

@@ -1,65 +0,0 @@
# Calibration Guide
This guide offers a structured and comprehensive overview of the calibration process for OrcaSlicer.
It covers key aspects such as flow rate, pressure advance, temperature towers, retraction tests, and advanced calibration techniques. Each section includes step-by-step instructions and visuals to help you better understand and carry out each calibration effectively.
To access the calibration features, you can find them in the **Calibration** section of the OrcaSlicer interface.
![calibration](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/calibration.png?raw=true)
> [!IMPORTANT]
> After completing the calibration process, remember to create a new project in order to exit the calibration mode.
The recommended order for calibration is as follows:
1. **[Temperature](temp-calib):** Start by calibrating the temperature of the nozzle and the bed. This is crucial as it affects the viscosity of the filament, which in turn influences how well it flows through the nozzle and adheres to the print bed.
<img alt="temp-tower" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Temp-calib/temp-tower.jpg?raw=true" height="200">
2. **[Flow](flow-rate-calib):** Calibrate the flow rate to ensure that the correct amount of filament is being extruded. This is important for achieving accurate dimensions and good layer adhesion.
<img alt="flowcalibration-example" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowcalibration-example.png?raw=true" height="200">
3. **[Pressure Advance](pressure-advance-calib):** Calibrate the pressure advance settings to improve print quality and reduce artifacts caused by pressure fluctuations in the nozzle.
- **[Adaptive Pressure Advance](adaptive-pressure-advance-calib):** This is an advanced calibration technique that can be used to further optimize the pressure advance settings for different print speeds and geometries.
<img alt="pa-tower" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-tower.jpg?raw=true" height="200">
4. **[Retraction](retraction-calib):** Calibrate the retraction settings to minimize stringing and improve print quality. Doing this after Flow and Pressure Advance calibration is recommended, as it ensures that the printer is already set up for optimal extrusion.
<img alt="retraction_test_print" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/retraction/retraction_test_print.jpg?raw=true" height="200">
5. **[Max Volumetric Speed](volumetric-speed-calib):** Calibrate the maximum volumetric speed of the filament. This is important for ensuring that the printer can handle the flow rate of the filament without causing issues such as under-extrusion or over-extrusion.
<img alt="mvf_measurement_point" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/MVF/mvf_measurement_point.jpg?raw=true" height="200">
6. **[Cornering](cornering-calib):** Calibrate the Jerk/Junction Deviation settings to improve print quality and reduce artifacts caused by sharp corners and changes in direction.
<img alt="jd_second_print_measure" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_second_print_measure.jpg?raw=true" height="200">
7. **[Input Shaping](input-shaping-calib):** This is an advanced calibration technique that can be used to reduce ringing and improve print quality by compensating for mechanical vibrations in the printer.
<img alt="IS_damp_marlin_print_measure" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_marlin_print_measure.jpg?raw=true" height="200">
8. **[VFA](vfa-calib):** A VFA speed test is available to find resonance speeds.
<img alt="vfa_test_print" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/vfa/vfa_test_print.jpg?raw=true" height="200">
---
**[Tolerance](tolerance-calib):** Calibrate the tolerances of your printer to ensure that it can accurately reproduce the dimensions of the model being printed. This is important for achieving a good fit between parts and for ensuring that the final print meets the desired specifications.
<img alt="OrcaToleranceTes_m6" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/OrcaToleranceTes_m6.jpg?raw=true" height="200">
---
_Credits:_
- _The Flow test and retraction test is inspired by [SuperSlicer](https://github.com/supermerill/SuperSlicer)._
- _The PA Line method is inspired by [K-factor Calibration Pattern](https://marlinfw.org/tools/lin_advance/k-factor.html)._
- _The PA Tower method is inspired by [Klipper](https://www.klipper3d.org/Pressure_Advance.html)._
- _The temp tower model is remixed from [Smart compact temperature calibration tower](https://www.thingiverse.com/thing:2729076)._
- _The max flowrate test was inspired by Stefan (CNC Kitchen), and the model used in the test is a remix of his [Extrusion Test Structure](https://www.printables.com/model/342075-extrusion-test-structure)._
- _ZV Input Shaping is inspired by [Marlin Input Shaping](https://marlinfw.org/docs/features/input_shaping.html) and [Ringing Tower 3D STL](https://marlinfw.org/assets/stl/ringing_tower.stl)._

View File

@@ -1,221 +0,0 @@
# Adaptive Pressure Advance
This feature aims to dynamically adjust the printers pressure advance to better match the conditions the toolhead is facing during a print. Specifically, to more closely align to the ideal values as flow rate, acceleration, and bridges are encountered.
This wiki page aims to explain how this feature works, the prerequisites required to get the most out of it as well as how to calibrate it and set it up.
## Settings Overview
This feature introduces the below options under the filament settings:
1. **Enable adaptive pressure advance:** This is the on/off setting switch for adaptive pressure advance.
2. **Enable adaptive pressure advance for overhangs:** Enable adaptive PA for overhangs as well as when flow changes within the same feature. This is an experimental option because if the PA profile is not set accurately, it will cause uniformity issues on the external surfaces before and after overhangs. It is recommended to start with this option switched off and enable it after the core adaptive pressure advance feature is calibrated correctly.
3. **Pressure advance for bridges:** Sets the desired pressure advance value for bridges. Set it to 0 to disable this feature. Experiments have shown that a lower PA value when printing bridges helps reduce the appearance of slight under extrusion immediately after a bridge, which is caused by the pressure drop in the nozzle when printing in the air. Therefore, a lower pressure advance value helps counteract this. A good starting point is approximately half your usual PA value.
4. **Adaptive pressure advance measurements:** This field contains the calibration values used to generate the pressure advance profile for the nozzle/printer. Input sets of pressure advance (PA) values and the corresponding volumetric flow speeds and accelerations they were measured at, separated by a comma. Add one set of values per line. More information on how to calibrate the model follows in the sections below.
5. **Pressure advance:** The old field is still needed and is required to be populated with a PA value. A “good enough” median PA value should be entered here, as this will act as a fallback value when performing tool changes, printing a purge/wipe tower for multi-color prints as well as a fallback in case the model fails to identify an appropriate value (unlikely but its the ultimate backstop).
![apa-material-config](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-material-config.png?raw=true)
## Pre-Requisites
This feature has been tested with Klipper-based printers. While it may work with Marlin or Bambu lab printers, it is currently untested with them. It shouldn't adversely affect the machine; however, the quality results from enabling it are not validated.
> [!IMPORTANT]
> Versions of Klipper older than July 11th, 2024 might in some cases produce artifacts when dynamic PA is used.
> This has been fixed in Klipper stable version 0.13.0 (20250411), or in developer versions starting [from July 11th, 2024](https://github.com/Klipper3d/klipper/commit/c84d78f3f169bc5163d11b74837f9880b0b7dba4).
> If you experience issues or print quality lower than expected you might want to ensure you have a Klipper firmware which contains the fix.
> [!TIP]
> **Advanced users**: the fix to ensure optimal quality when dynamic PA is used does not require flashing the toolhead MCU or the printer MCU, but only an update of the host Python code. If you are using a commercial printer which does not offer straightforward MCU flashing (for example, Qidi printers), you might be able to manually patch the host code yourself.
## Use case (what to expect)
Following experimentation, it has been noticed that the optimal pressure advance value is less:
1. The faster you print (hence the higher the volumetric flow rate requested from the toolhead).
2. The larger the layer height (hence the higher the volumetric flow rate requested from the toolhead).
3. The higher the print acceleration is.
What this means is that we never get ideal PA values for each print feature, especially when they vary drastically in speed and acceleration. We can tune PA for a faster print speed (flow) but compromise on corner sharpness for slower speeds or tune PA for corner sharpness and deal with slight corner-perimeter separation in faster speeds. The same goes for accelerations as well as different layer heights.
This compromise usually means that we settle for tuning an "in-between" PA value between slower external features and faster internal features so we don't get gaps, but also not get too much bulging in external perimeters.
**However, what this also means is that if you are printing with a single layer height, single speed, and acceleration, there is no need to enable this feature.**
Adaptive pressure advance aims to address this limitation by implementing a completely different method of setting pressure advance. **Following a set of PA calibration tests done at different flow rates (speeds and layer heights) and accelerations, a pressure advance model is calculated by the slicer.** Then that model is used to emit the best fit PA for any arbitrary feature flow rate (speed) and acceleration used in the print process.
In addition, it means that you only need to tune this feature once and print across different layer heights with good PA performance.
Finally, if during calibration you notice that there is little to no variance between the PA tests, this feature is redundant for you. **From experiments, high flow nozzles fitted on high-speed core XY printers appear to benefit the most from this feature as they print with a larger range of flow rates and at a larger range of accelerations.**
### Expected results
With this feature enabled there should be absolutely no bulge in the corners, just the smooth rounding caused by the square corner velocity of your printer.
![apa-expected-results](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-expected-results.jpg?raw=true)
In addition, seams should appear smooth with no bulging or under extrusion.
![apa-expected-seam](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-expected-seam.jpg?raw=true)
Solid infill should have no gaps, pinholes, or separation from the perimeters.
![apa-expected-solid-infill](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-expected-solid-infill.jpg?raw=true)
Compared to with this feature disabled, where the internal solid infill and external-internal perimeters show signs of separation and under extrusion, when PA is tuned for optimal external perimeter performance as shown below.
![apa-unexpected-solid-infill](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-unexpected-solid-infill.jpg?raw=true)
## How to calibrate the adaptive pressure advance model
### Defining the calibration sets
Firstly, it is important to understand your printer speed and acceleration limits in order to set meaningful boundaries for the calibrations:
1. **Upper acceleration range:** Do not attempt to calibrate adaptive PA for an acceleration that is larger than what the Klipper input shaper calibration tool recommends for your selected shaper. For example, if Klipper recommends an EI shaper with 4k maximum acceleration for your slowest axis (usually the Y axis), dont calibrate adaptive PA beyond that value. This is because after 4k the input shaper smoothing is magnified and the perimeter separations that appear like PA issues are caused by the input shaper smoothing the shape of the corner. Basically, youd be attempting to compensate for an input shaper artefact with PA.
2. **Upper print speed range:** The Ellis PA pattern test has been proven to be the most efficient and effective test to run to calibrate adaptive PA. It is fast and allows for a reasonably accurate and easy-to-read PA value. However, the size of the line segments is quite small, which means that for the faster print speeds and slower accelerations, the toolhead will not be able to reach the full flow rate that we are calibrating against. It is therefore generally not recommended to attempt calibration with a print speed of higher than ~200-250mm/sec and accelerations slower than 1k in the PA pattern test. If your lowest acceleration is higher than 1k, then proportionally higher maximum print speeds can be used.
**Remember:** With the calibration process, we aim to create a PA Flow Rate Acceleration profile for the toolhead. As we cannot directly control flow rate, we use print speed as a proxy (higher speed -> higher flow).
With the above in mind, lets create a worked example to identify the optimal number of PA tests to calibrate the adaptive PA model.
**The below starting points are recommended for the majority of Core XY printers:**
1. **Accelerations:** 1k, 2k, 4k
2. **Print speeds:** 50mm/sec, 100mm/sec, 150mm/sec, 200mm/sec.
**That means we need to run 3x4 = 12 PA tests and identify the optimal PA for them.**
Finally, if the maximum acceleration given by input shaper is materially higher than 4k, run a set of tests with the higher accelerations. For example, if input shaper allows a 6k value, run PA tests as below:
1. **Accelerations:** 1k, 2k, 4k, 6k
2. **Print speeds:** 50mm/sec, 100mm/sec, 150mm/sec, 200mm/sec.
Similarly, if the maximum value recommended is 12k, run PA tests as below:
1. **Accelerations:** 1k, 2k, 4k, 8k, 12k
2. **Print speeds:** 50mm/sec, 100mm/sec, 150mm/sec, 200mm/sec.
So, at worst case you will need to run 5x4 = 20 PA tests if your printer acceleration is on the upper end! In essence, you want enough granularity of data points to create a meaningful model while also not overdoing it with the number of tests. So, doubling the speed and acceleration is a good compromise to arrive at the optimal number of tests.
For this example, lets assume that the baseline number of tests is adequate for your printer:
1. **Accelerations:** 1k, 2k, 4k
2. **Print speeds:** 50mm/sec, 100mm/sec, 150mm/sec, 200mm/sec.
We, therefore, need to run 12 PA tests as below:
| Speed | Acceleration |
|-------|--------------|
| 50 | 1k |
| 100 | 1k |
| 150 | 1k |
| 200 | 1k |
| 50 | 2k |
| 100 | 2k |
| 150 | 2k |
| 200 | 2k |
| 50 | 4k |
| 100 | 4k |
| 150 | 4k |
| 200 | 4k |
### Identifying the flow rates from the print speed
#### OrcaSlicer 2.2.0 and later
Test parameters needed to build adaptive PA table are printed on the test sample:
![apa-test](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-test.png?raw=true)
Test sample above was done with acceleration 12000 mm/s² and flow rate 27.13 mm³/s
#### OrcaSlicer 2.1.0 and older
As mentioned earlier, **the print speed is used as a proxy to vary the extrusion flow rate**. Once your PA test is set up, change the gcode preview to “flow” and move the horizontal slider over one of the herringbone patterns and take note of the flow rate for different speeds.
![apa-test210](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-test210.png?raw=true)
### Running the tests
#### General tips
It is recommended that the PA step is set to a small value, to allow you to make meaningful distinctions between the different tests **therefore a PA step value of 0.001 is recommended**.
**Set the end PA to a value high enough to start showing perimeter separation for the lowest flow (print speed) and acceleration test.** For example, for a Voron 350 using Revo HF, the maximum value was set to 0.05 as that was sufficient to show perimeter separation even at the slowest flow rates and accelerations.
**If the test is too big to fit on the build plate, increase your starting PA value or the PA step value accordingly until the test can fit.** If the lowest value becomes too high and there is no ideal PA present in the test, focus on increasing the PA step value to reduce the number of herringbones printed (hence the size of the print).
![pa-pattern-general](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-general.png?raw=true)
#### OrcaSlicer 2.3.0 and newer
PA pattern calibration configuration window have been changed to simplify test setup. Now all is needed is to fill list of accelerations and speeds into relevant fields of the calibration window:
![pa-pattern-batch](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-batch.png?raw=true)
Test patterns generated for each acceleration-speed pair and all parameters are set accordingly. No additional actions needed from user side. Just slice and print all plates generated.
Refer to [Calibration Guide](Calibration) for more details on batch mode calibration.
#### OrcaSlicer 2.2.0 and older
Setup your PA test as usual from the calibration menu in OrcaSlicer. Once setup, your PA test should look like the below:
![apa-setup-result-speed](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-setup-result-speed.png?raw=true)
![apa-setup-result-acceleration-jerk](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-setup-result-acceleration-jerk.png?raw=true)
Now input your identified print speeds and accelerations in the fields above and run the PA tests.
> [!IMPORTANT]
> Make sure your acceleration values are all the same in all text boxes. Same for the print speed values and Jerk (XY) values. Make sure your Jerk value is set to the external perimeter jerk used in your print profiles.
#### Test results processing
Now run the tests and note the optimal PA value, the flow, and the acceleration. You should produce a table like this:
| Speed | Flow | Acceleration | PA | Model values |
|-------|-------|--------------|-------|----------------------|
| 50 | 3.84 | 1000 | 0.036 | 0.036 , 3.84 , 1000 |
| 100 | 7.68 | 1000 | 0.036 | 0.036 , 7.68 , 1000 |
| 150 | 11.51 | 1000 | 0.036 | 0.036 , 11.51 , 1000 |
| 200 | 15.35 | 1000 | 0.036 | 0.036 , 15.35 , 1000 |
| | | | | |
| 50 | 3.84 | 2000 | 0.036 | 0.036 , 3.84 , 2000 |
| 100 | 7.68 | 2000 | 0.03 | 0.03 , 7.68 , 2000 |
| 150 | 11.51 | 2000 | 0.029 | 0.029 , 11.51 , 2000 |
| 200 | 15.35 | 2000 | 0.028 | 0.028 , 15.35 , 2000 |
| | | | | |
| 50 | 3.84 | 4000 | 0.032 | 0.032 , 3.84 , 4000 |
| 100 | 7.68 | 4000 | 0.028 | 0.028 , 7.68 , 4000 |
| 150 | 11.51 | 4000 | 0.026 | 0.026 , 11.51 , 4000 |
| 200 | 15.35 | 4000 | 0.024 | 0.024 , 15.35 , 4000 |
Concatenate the PA value, the flow value, and the acceleration value into the final comma-separated sets to create the values entered in the model as shown above.
**Youre now done! The PA profile is created and calibrated!**
Remember to paste the values in the adaptive pressure advance measurements text box as shown below, and save your filament profile.
![apa-profile](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-profile.png?raw=true)
### Tips
#### Model input
The adaptive PA model built into the slicer is flexible enough to allow for as many or as few increments of flow and acceleration as you want. Ideally, you want at a minimum 3x data points for acceleration and flow in order to create a meaningful model.
However, if you dont want to calibrate for flow, just run the acceleration tests and leave flow the same for each test (in which case youll input only 3 rows in the model text box). In this case, flow will be ignored when the model is used.
Similarly for acceleration in the above example youll input only 4 rows in the model text box, in which case acceleration will be ignored when the model is used.
**However, make sure a triplet of values is always provided PA value, Flow, Acceleration.**
#### Identifying the right PA
Higher acceleration and higher flow rate PA tests are easier to identify the optimal PA as the range of “good” values is much narrower. Its evident where the PA is too large, as gaps start to appear in the corner and where PA is too low, as the corner starts bulging.
However, the lower the flow rate and accelerations are, the range of good values is much wider. Having examined the PA tests even under a microscope, what is evident, is that if you cant distinguish a value as being evidently better than another (i.e. sharper corner with no gaps) with the naked eye, then both values are correct. In which case, if you cant find any meaningful difference, simply use the optimal values from the higher flow rates.
- **Too high PA**
![apa-identify-too-high](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-identify-too-high.jpg?raw=true)
- **Too low PA**
![apa-identify-too-low](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-identify-too-low.jpg?raw=true)
- **Optimal PA**
![apa-identify-optimal](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/apa-identify-optimal.jpg?raw=true)

View File

@@ -1,133 +0,0 @@
# Cornering
Cornering is a critical aspect of 3D printing that affects print quality and accuracy. It's how the printer handles changes in direction during movement, particularly at corners and curves. Proper cornering settings can reduce artifacts such as ringing, ghosting, and overshooting, resulting in cleaner and more precise prints.
## Types of Cornering Settings
> [!TIP]
> Read more in [Jerk XY](speed_settings_jerk_xy) and check [Cornering Control Types](speed_settings_jerk_xy#cornering-control-types)
## Calibration
This test will be set detect automatically your printer firmware type and will adapt to the specific calibration process.
- Klipper: [square_corner_velocity](https://www.klipper3d.org/Config_Reference.html#printer)
- Marlin 2:
- [Junction Deviation](https://marlinfw.org/docs/configuration/configuration.html#junction-deviation-) if `Maximum Junction Deviation` in Printer settings/Motion ability/Jerk limitations is bigger than `0`.
- [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) if `Maximum Junction Deviation` is set to `0`.
- Marlin Legacy: [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-).
- RepRap: [Maximum instantaneous speed changes](https://docs.duet3d.com/User_manual/Reference/Gcodes#m566-set-allowable-instantaneous-speed-change)
> [!NOTE]
> This calibration example uses Junction Deviation as an example. The process is similar for Jerk calibration; just read the Jerk values instead of JD values.
> JD values are between `0.0` and `0.3` (in mm) while Jerk values are usually between `1` and `20` or higher (in mm/s).
1. Pre-requisites:
1. If using Marlin 2 firmware, Check if your printer has Junction Deviation enabled. Look for `Junction deviation` in the printer's advanced settings.
2. In OrcaSlicer, set:
1. Acceleration high enough to trigger ringing or the speed you want to check out (e.g., 2000 mm/s²).
2. Speed high enough to trigger ringing (e.g., 100 mm/s).
3. Use an opaque, high-gloss filament to make ringing more visible.
2. Open the Cornering test.
![jd_first_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_first_menu.png?raw=true)
1. In this first approximation, set a wide range of Start and End values.
- If you don't see any loss of quality, increase the End value and retry.
- If you do see a loss of quality, measure the maximum height when the corners start losing sharpness and read the Cornering/Jerk/JunctionDeviation value set at that point in OrcaSlicer.
![jd_first_print_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_first_print_measure.jpg?raw=true)
![jd_first_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_first_slicer_measure.png?raw=true)
2. Print a new calibration tower with a maximum set near the point where corners start losing sharpness.
**RECOMMENDED:** Use the *Ringing Tower* test model to more easily visualize the jerk limit.
3. Print the second Cornering test with the new maximum value.
![jd_second_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_second_menu.png?raw=true)
4. Measure the maximum height when the corners start losing sharpness and read the Cornering/Jerk/JunctionDeviation value set at that point in OrcaSlicer.
![jd_second_print_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_second_print_measure.jpg?raw=true)
![jd_second_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_second_slicer_measure.png?raw=true)
3. Save the settings
- Into your OrcaSlicer printer profile (**RECOMMENDED**):
1. Go to Printer settings → Motion ability → Jerk limitation:
2. Set your maximum Jerk X and Y or Junction Deviation values.
![jd_printer_jerk_limitation](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/JunctionDeviation/jd_printer_jerk_limitation.png?raw=true)
- Directly into your printer firmware:
- Restore your 3D Printer settings to avoid keeping high acceleration and jerk values used for the test.
- Klipper:
- Skeleton
```gcode
SET_VELOCITY_LIMIT SQUARE_CORNER_VELOCITY=#SquareCornerVelocity
```
Example:
```gcode
SET_VELOCITY_LIMIT SQUARE_CORNER_VELOCITY=5.0
```
Note: You can also set `square_corner_velocity` persistently in your `printer.cfg` (restart required).
- Marlin 2 (Junction Deviation enabled):
- Skeleton
```gcode
M205 J#JunctionDeviationValue
M500
```
Example:
```gcode
M205 J0.012
M500
```
- To make the change permanent in firmware, set in `Configuration.h` and recompile:
```cpp
#define JUNCTION_DEVIATION_MM 0.012 // (mm) Distance from real junction edge
```
Also ensure classic jerk is disabled if using junction deviation:
```cpp
//#define CLASSIC_JERK
```
- Marlin Classic Jerk / Marlin Legacy:
- Skeleton — set the per-axis jerk limits using `M205` (X/Y optional depending on firmware build):
```gcode
M205 X#JerkX Y#JerkY
M500
```
Example:
```gcode
M205 X10 Y10
M500
```
- RepRap (Duet / RepRapFirmware):
**IMPORTANT:** Set in mm/min so convert from mm/s to mm/min multiply by 60.
- Skeleton
```gcode
M566 X#max_instantaneous_change Y#max_instantaneous_change
M500 ; if supported by your board
```
Example (Duet-style):
```gcode
M566 X3000 Y3000
```
> [!NOTE]
> RepRapFirmware exposes `M566` to set allowable instantaneous speed changes; some boards may persist settings with `M500` or via their web/config files.
## Credits
- **Junction Deviation Machine Limit** [@RF47](https://github.com/RF47)
- **Cornering Calibration** [@IanAlexis](https://github.com/IanAlexis)
- **Fast tower model** [@RF47](https://github.com/RF47)
- **SCV-V2 model** [@chrisheib](https://www.thingiverse.com/chrisheib)

View File

@@ -1,80 +0,0 @@
# Flow Rate Calibration
Flow ratio determines how much filament is extruded and is crucial for high-quality prints.
A properly calibrated flow ratio ensures consistent layer adhesion and accurate dimensions.
- Too **low** flow ratio causes under-extrusion, which leads to gaps, weak layers, and poor structural integrity.
- Too **high** flow ratio causes over-extrusion, resulting in excess material, rough surfaces, and dimensional inaccuracies.
- [Calibration Types](#calibration-types)
- [OrcaSlicer \> 2.3.0 Archimedean chords + YOLO (Recommended)](#orcaslicer--230-archimedean-chords--yolo-recommended)
- [OrcaSlicer \<= 2.3.0 Monotonic Line + 2-Pass Calibration](#orcaslicer--230-monotonic-line--2-pass-calibration)
- [Credits](#credits)
> [!WARNING]
> **BambuLab Printers:** Make sure you do **not** select the 'Flow calibration' option.
> ![flowrate-Bambulab-uncheck](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowrate-Bambulab-uncheck.png?raw=true)
> [!NOTE]
> After v2.3.0, the [Top Pattern](strength_settings_top_bottom_shells#surface-pattern) changed to [Archimedean chords](strength_settings_patterns#archimedean-chords) from [Monotonic Line](strength_settings_patterns#monotonic-line).
## Calibration Types
- **YOLO:** A simplified method that adjusts the flow rate in a single pass using the formula `OldFlowRatio ± modifier`.
- **Recommended:** calibration range `[-0.05, +0.05]`, flow rate step `0.01`.
- **Perfectionist:** calibration range `[-0.04, +0.035]`, flow rate step `0.005`.
- **2-Pass Calibration:** the legacy method, using two passes to determine the optimal flow rate with the formula `OldFlowRatio * (100 + modifier) / 100`.
### OrcaSlicer > 2.3.0 Archimedean chords + YOLO (Recommended)
This method uses the [Archimedean Chords](strength_settings_patterns#archimedean-chords) pattern for flow rate calibration with the YOLO (Recommended) approach.
1. Select the printer and the filament you want to calibrate.
This method is based on the filament's current flow ratio, so make sure you select the correct filament before proceeding.
2. In the `Calibration` menu, under the `Flow Rate` section, select `YOLO (Recommended)`.
3. A new project with eleven blocks will be created, each with a different flow rate modifier. Slice and print the project.
![flowcalibration-yolo](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowcalibration-yolo.gif?raw=true)
4. Examine the printed blocks and identify the one with the best surface quality. Look for:
1. The smoothest top surface.
2. No visible gaps between the pattern arcs.
3. Minimal or no visible line between the Inner Spiral and the Outer Arcs.
![flowcalibration-guide](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowcalibration-guide.png?raw=true)
In this example, the block with a flow modifier of `+0.01` produced the best results, despite a visible line between the Inner Spiral and the Outer Arcs; reducing the flow further begins to show gaps between the lines.
![flowcalibration-example](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowcalibration-example.png?raw=true)
5. Update the flow ratio in the filament settings using the equation: `OldFlowRatio ± modifier`.
If your previous flow ratio was `0.98` and you selected the block with a flow rate modifier of `+0.01`, the new value would be: `0.98 + 0.01 = 0.99`.
**Remember** to save the filament profile.
![flowcalibration_update_flowrate](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowcalibration_update_flowrate.png?raw=true)
> [!NOTE]
> The new Archimedean chords pattern uses a specific print order that prints the inner spiral last so you can check for material accumulation on the contact line at the end.
### OrcaSlicer <= 2.3.0 Monotonic Line + 2-Pass Calibration
This example uses the Monotonic Line pattern with the 2-Pass Calibration approach.
![flow-calibration-monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flow-calibration-monotonic.gif?raw=true)
1. Select the printer, filament, and process you want to use for the test.
2. In the `Calibration` menu, under the `Flow Rate` section, select `Pass 1`.
3. A new project with nine blocks will be created, each with a different flow rate modifier. Slice and print the project.
4. Examine the blocks and determine which one has the smoothest top surface.
![flowrate-pass1-monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flowrate-pass1-monotonic.jpg?raw=true)
![flowrate-0-5-monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flowrate-0-5-monotonic.jpg?raw=true)
5. Update the flow ratio in the filament settings using the equation: `OldFlowRatio * (100 + modifier) / 100`.
For example, if your previous flow ratio was `0.98` and you selected the block with a flow rate modifier of `+5`, the new value would be: `0.98 × (100 + 5) / 100 = 1.029`.
**Remember** to save the filament profile.
6. Perform the `Pass 2` calibration. This process is similar to `Pass 1`, but a new project with ten blocks will be generated. The flow rate modifiers for this project will range from `-9` to `0`.
7. Repeat steps 4 and 5. For example, if your previous flow ratio was `1.029` and you selected the block with a flow rate modifier of `-6`, the new value would be: `1.029 × (100 - 6) / 100 = 0.96726`.
**Remember** to save the filament profile.
![flowrate-pass2-monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flowrate-pass2-monotonic.jpg?raw=true)
![flowrate-6-monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flowrate-6-monotonic.jpg?raw=true)
![flowcalibration_update_flowrate_monotonic](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/monotonic-flow-rate/flowcalibration_update_flowrate_monotonic.png?raw=true)
> [!TIP]
> @ItsDeidara has created an HTML tool to help with these calculations. Check it out if you find the equations confusing: [Orca-Slicer-Assistant](https://github.com/ItsDeidara/Orca-Slicer-Assistant).
## Credits
- **[Archimedean Chords Idea](https://makerworld.com/es/models/189543-improved-flow-ratio-calibration-v3#profileId-209504)**: [Jimcorner](https://makerworld.com/es/@jimcorner)

View File

@@ -1,131 +0,0 @@
# Input Shaping
During high-speed movements, vibrations can cause a phenomenon called "ringing," where periodic ripples appear on the print surface. Input Shaping provides an effective solution by counteracting these vibrations, improving print quality and reducing wear on components without needing to significantly lower print speeds.
> [!IMPORTANT]
> RepRap can only set one frequency for both X and Y axes so you will need to select a frequency that works well for both axes.
- [Types](#types)
- [Default](#default)
- [Version Table](#version-table)
- [Calibration Steps](#calibration-steps)
- [Fixed-Time Motion](#fixed-time-motion)
- [Credits](#credits)
## Types
It is usually recommended to use MZV, EI (specially for Delta printers) or ZV as a simple and effective solution.
Not all Input Shaping types are available in all firmware and their performance may vary depending on the firmware implementation and the printer's mechanics.
### Default
When "Default" is selected, the firmware's default input shaper will be used.
Every firmware and even its version may have a different default type but usually are:
- Klipper: MZV
- Marlin: ZV
- RepRap:
- Version >= 3.4: MZV
- Version < 3.4: DAA
- Version < 3.2: DAA (without damping option)
### Version Table
| Type | Name | [Klipper](https://www.klipper3d.org/Resonance_Compensation.html#technical-details) | [RepRap](https://docs.duet3d.com/User_manual/Reference/Gcodes#m593-configure-input-shaping) | [Marlin 2](https://marlinfw.org/docs/features/ft_motion.html#more-complexity-zv-input-shaper) | Marlin Legacy |
|---|---|---|---|---|---|
| MZV | Modified Zero Vibration | >=0.9.0 | >=3.4 | - | - |
| ZV | Zero Vibration | >=0.9.0 | = 3.5 | >2.1.2 | - |
| ZVD | Zero Vibration Derivative | >=0.9.0 | >=3.4 | - | - |
| ZVDD | Zero Vibration Double Derivative | - | >=3.4 | - | - |
| ZVDDD | Zero Vibration Triple Derivative | - | >=3.4 | - | - |
| EI | Extra Insensitive | >=0.9.0 | - | - | - |
| 2HUMP_EI / EI2 | Two-Hump Extra Insensitive | >=0.9.0 | >=3.4 | - | - |
| 3HUMP_EI / EI3 | Three-Hump Extra Insensitive | >=0.9.0 | >=3.4 | - | - |
| [FT_MOTION](https://marlinfw.org/docs/features/ft_motion.html#fixed-time-motion-by-ulendo) | Fixed-Time Motion | - | - | >2.1.3 | - |
| DAA | Damped Anti-Resonance | - | < 3.4 | - | - |
## Calibration Steps
0. Pre-requisites:
1. Use an opaque, high-gloss filament to make the ringing more visible.
2. In OrcaSlicer, set:
1. Acceleration high enough to trigger ringing (e.g., 20000 mm/s²).
2. Speed high enough to trigger ringing (e.g., 200 mm/s).
> [!IMPORTANT]
> It's recommended to use the fastest [acceleration](speed_settings_acceleration), [speed](speed_settings_other_layers_speed) and [Jerk/Junction Deviation](speed_settings_jerk_xy) your printer can handle without losing steps.
> This test **will set the values to high values** limited by your printer's motion ability and the filament's max volumetric speed (avoid materials below 10 mm³/s).
1. Select the Test Model ´Ringing Tower´ (Recommended) or ´Fast Tower´ (Reduced version useful for printers with high ringing).
2. Select the [Input Shaper Type](#types) you want to test. Each firmware has different types available and each type has different performance.
3. Select a range of frequencies to test. The Default 15hz to 110hz range is usually a good start.
4. Select your damping. Usually, a value between 0.1 and 0.2 is a good start but you can change it to 0 and your printer will use the firmware default value (if available).
![IS_freq_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_freq_menu.png?raw=true)
1. Measure the X and Y heights and read the frequency set at that point in OrcaSlicer.
![IS_freq_marlin_print_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_freq_marlin_print_measure.jpg?raw=true)
- Marlin:
![IS_freq_marlin_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_freq_marlin_slicer_measure.png?raw=true)
- Klipper:
![IS_freq_klipper_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_freq_klipper_slicer_measure.png?raw=true)
2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value.
5. Print the Damping test setting your X and Y frequency to the value you found in the previous step.
![IS_damp_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_menu.png?raw=true)
1. Measure the X and Y heights and read the damping set at that point in OrcaSlicer.
![IS_damp_marlin_print_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_marlin_print_measure.jpg?raw=true)
- Marlin:
![IS_damp_marlin_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_marlin_slicer_measure.png?raw=true)
- Klipper:
![IS_damp_klipper_slicer_measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_klipper_slicer_measure.png?raw=true)
6. **Restore your 3D Printer settings to avoid keep using high acceleration and jerk values.**
7. Save the settings
- Into your printer firmware settings save the values you found (Type, frequency/cies and damp)
- Save it into Orca's printer profile settings in Printer settings/ Machine G-code/ Machine start G-code using the following G-code:
- Klipper:
- Skeleton
```gcode
SET_INPUT_SHAPER SHAPER_TYPE=TYPE SHAPER_FREQ_X=#Xfrequency DAMPING_RATIO_X=#XDamping SHAPER_FREQ_Y=#Yfrequency DAMPING_RATIO_Y=#YDamping
```
Example
```gcode
SET_INPUT_SHAPER SHAPER_TYPE=MZV SHAPER_FREQ_X=37.25 DAMPING_RATIO_X=0.16 SHAPER_FREQ_Y=37.5 DAMPING_RATIO_Y=0.06
```
- Marlin:
- Skeleton
```gcode
M593 X F#Xfrequency D#XDamping
M593 Y F#Yfrequency D#YDamping
M500
```
Example
```gcode
M593 X F37.25 D0.16
M593 Y F37.5 D0.06
M500
```
- RepRap:
- Skeleton for RepRap 3.3 and later
```gcode
M593 P#Type F#frequency S#Damping
```
Example RepRap 3.4 and later
```gcode
M593 P"ZVD" F37.25 S0.16
```
- Skeleton for RepRap 3.2 and earlier
```gcode
M593 F#frequency
```
Example Legacy (RepRap 3.2 and earlier)
```gcode
M593 F37.25
```
### Fixed-Time Motion
TODO: This calibration test is currently under development. See the [Marlin documentation](https://marlinfw.org/docs/gcode/M493.html) for more information.
## Credits
- **Input Shaping Calibration:** [@IanAlexis](https://github.com/IanAlexis) and [@RF47](https://github.com/RF47)
- **Klipper testing:** [@ShaneDelmore](https://github.com/ShaneDelmore)

View File

@@ -1,89 +0,0 @@
# Pressure Advance
Pressure Advance is a feature that compensates for the lag in filament pressure within the nozzle during acceleration and deceleration. It helps improve print quality by reducing issues like blobs, oozing, and inconsistent extrusion, especially at corners or during fast movements.
OrcaSlicer includes three approaches for calibrating the Pressure Advance value. Each method has its own advantages and disadvantages. It is important to note that each method has two versions: one for a direct-drive extruder and one for a Bowden extruder. Make sure to select the appropriate version for your test.
> [!WARNING]
> **Marlin Printers:** Linear Advance must be enabled in firmware (M900).
> **Not all printers have it enabled by default.**
> [!WARNING]
> **Bambulab Printers:** make sure you do not select the 'Flow calibration' option.
> ![flowrate-Bambulab-uncheck](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Flow-Rate/flowrate-Bambulab-uncheck.png?raw=true)
- [Calibration](#calibration)
- [Tower method](#tower-method)
- [Pattern method](#pattern-method)
- [Line method](#line-method)
## Calibration
You can use different methods to calibrate the Pressure Advance value, each with its own advantages and disadvantages.
The results from these methods should be saved to the material profile.
![pressure_advance_enable](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pressure_advance_enable.png?raw=true)
> [!TIP]
> Consider using the [Adaptive Pressure Advance](adaptive-pressure-advance-calib) method for more accurate results.
> Especially for high-speed printers.
### Tower method
The tower method may take a bit more time to complete, but it does not rely on the quality of the first layer.
1. Select the printer, filament, and process you would like to use for the test.
2. Examine each corner of the print and mark the height that yields the best overall result.
3. In this example a height of 8 mm was selected, so the Pressure Advance value should be calculated as `PressureAdvanceStart + (PressureAdvanceStep x measured)`; example: `0 + (0.002 x 8) = 0.016`.
![pa-tower](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-tower.jpg?raw=true)
![pa-tower-measure](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-tower-measure.jpg?raw=true)
> [!TIP]
> @ItsDeidara has made an HTML tool to help with the calculation. Check it out if those equations give you a headache [here](https://github.com/ItsDeidara/Orca-Slicer-Assistant).
### Pattern method
The pattern method is adapted from [Andrew Ellis' pattern method generator](https://ellis3dp.com/Pressure_Linear_Advance_Tool/), which was itself derived from the [Marlin pattern method](https://marlinfw.org/tools/lin_advance/k-factor.html) developed by [Sineos](https://github.com/Sineos/k-factorjs).
[Instructions for using and reading the pattern method](https://ellis3dp.com/Print-Tuning-Guide/articles/pressure_linear_advance/pattern_method.html) are provided in [Ellis' Print Tuning Guide](https://ellis3dp.com/Print-Tuning-Guide/), with only a few OrcaSlicer differences to note.
The test configuration window allows the user to generate one or more tests in a single project. Multiple tests will be placed on the plate with extra plates added if needed.
1. Single test \
![pa-pattern-single](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-single.png?raw=true)
2. Batch mode testing (multiple tests on a single plate) \
![pa-pattern-batch](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-batch.png?raw=true)
Once a test is generated, one or more small rectangular prisms will be placed on the plate, one for each test case. The prism object serves a few purposes:
1. The test pattern itself is added in as custom G-Code at each layer, same as you could do by hand. The rectangular prism provides the layers in which to insert that G-Code. This also means that **you'll see the full test pattern when you move to the Preview pane:**
![pa-pattern-batch-plater](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-batch-plater.png?raw=true)
2. The prism acts as a handle, enabling you to move the test pattern wherever you'd like on the plate by moving the prism.
3. Each test object is pre-configured with target parameters which are reflected in the object's name. Test parameters may be adjusted for each prism individually via the object list pane:
![pa-pattern-batch-objects](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-pattern-batch-objects.png?raw=true)
Next, Ellis' generator provided the ability to adjust specific printer, filament, and print profile settings. You can make these same changes in OrcaSlicer by adjusting the settings in the Prepare pane as you would with any other print. When you initiate the calibration test, Ellis' default settings are applied. A few things to note about these settings:
1. Ellis specified line widths as a percent of filament diameter. The Orca pattern method does the same to provide its suggested defaults, making use of Ellis' percentages in combination with your specified nozzle diameter.
2. In terms of line width, the pattern only makes use of the `Default` and `First layer` widths.
3. In terms of speed, the pattern only uses the `First layer speed -> First layer` and `Other layers speed -> Outer wall` speeds.
4. The infill pattern beneath the numbers cannot be changed because it's not actually an infill pattern pulled from the settings. All of the pattern G-Code is custom written, so that "infill" is, effectively, hand-drawn and not processed through the usual channels that would enable Orca to recognize it as infill.
### Line method
The line method is quick and straightforward to test. However, its accuracy depends heavily on the quality of your first layer. It is suggested to turn on bed mesh leveling for this test.
Steps:
1. Select the printer, filament, and process you would like to use for the test.
2. Print the project and check the result. Choose the value corresponding to the most even line and update your Pressure Advance value in the filament settings.
3. In this test, a Pressure Advance value of `0.016` appears to be optimal.
![pa-line](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-line.gif?raw=true)
![pa-lines](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-lines.png?raw=true)
![pa-line-0-016](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/pa/pa-line-0-016.png?raw=true)

View File

@@ -1,20 +0,0 @@
# Retraction test
Retraction is the process of pulling the filament back into the nozzle to prevent oozing and stringing during non-print moves. If the retraction length is too short, it may not effectively prevent oozing, while if it's too long, it can lead to clogs or under-extrusion. Filaments like PETG and TPU are more prone to stringing, so they may require longer retraction lengths compared to PLA or ABS.
This test generates a retraction tower automatically. The retraction tower is a vertical structure with multiple notches, each printed at a different retraction length. After the print is complete, we can examine each section of the tower to determine the optimal retraction length for the filament. The optimal retraction length is the shortest one that produces the cleanest tower.
![retraction_test](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/retraction/retraction_test.gif?raw=true)
![retraction_test_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/retraction/retraction_test_menu.png?raw=true)
In the dialog, you can select the start and end retraction length, as well as the retraction length increment step. The default values are 0mm for the start retraction length, 2mm for the end retraction length, and 0.1mm for the step. These values are suitable for most direct drive extruders. However, for Bowden extruders, you may want to increase the start and end retraction lengths to 1mm and 6mm, respectively, and set the step to 0.2mm.
![retraction_test_print](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/retraction/retraction_test_print.jpg?raw=true)
> [!NOTE]
> When testing filaments such as PLA or ABS that have minimal oozing, the retraction settings can be highly effective. You may find that the retraction tower appears clean right from the start. In such situations, setting the retraction length to 0.2mm - 0.4mm using OrcaSlicer should suffice.
> On the other hand, if there is still a lot of stringing at the top of the tower, it is recommended to dry your filament and ensure that your nozzle is properly installed without any leaks.
> [!TIP]
> @ItsDeidara has made a html to help with the calculation. Check it out if those equations give you a headache [here](https://github.com/ItsDeidara/Orca-Slicer-Assistant).

View File

@@ -1,69 +0,0 @@
# Temp Calibration
In FDM 3D printing, the temperature is a critical factor that affects the quality of the print.
There is no other calibration that can have such a big impact on the print quality as temperature calibration.
- [Standard Temperature Ranges](#standard-temperature-ranges)
- [Nozzle Temp tower](#nozzle-temp-tower)
- [Bed Temperature](#bed-temperature)
- [Chamber Temperature](#chamber-temperature)
## Standard Temperature Ranges
| Material | [Nozzle Temp (°C)](#nozzle-temp-tower) | [Bed Temp (°C)](#bed-temperature) | [Chamber Temp (°C)](#chamber-temperature) |
|:------------:|:--------------------------------------:|:---------------------------------:|:-----------------------------------------:|
| PLA | 180-220 | 50-60 | Ambient |
| ABS | 230-250 | 90-100 | 50-70 |
| ASA | 240-260 | 90-100 | 50-70 |
| Nylon 6 | 230-260 | 90-110 | 70-100 |
| Nylon 12 | 225-260 | 90-110 | 70-100 |
| TPU | 220-245 | 40-60 | Ambient |
| PC | 270-310 | 100-120 | 80-100 |
| PC-ABS | 260-280 | 95-110 | 60-80 |
| HIPS | 220-250 | 90-110 | 50-70 |
| PP | 220-270 | 80-105 | 40-70 |
| Acetal (POM) | 210-240 | 100-130 | 70-100 |
## Nozzle Temp tower
Nozzle temperature is one of the most important settings to calibrate for a successful print. The temperature of the nozzle affects the viscosity of the filament, which in turn affects how well it flows through the nozzle and adheres to the print bed. If the temperature is too low, the filament may not flow properly, leading to under-extrusion, poor layer adhesion and stringing. If the temperature is too high, the filament may degrade, over-extrude and produce stringing.
![temp-tower_test](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Temp-calib/temp-tower_test.gif?raw=true)
![temp-tower_test_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Temp-calib/temp-tower_test_menu.png?raw=true)
Temp tower is a straightforward test. The temp tower is a vertical tower with multiple blocks, each printed at a different temperature.
Once the print is complete, we can examine each block of the tower and determine the optimal temperature for the filament. The optimal temperature is the one that produces the highest quality print with the least amount of issues, such as stringing, layer adhesion, warping (overhang), and bridging.
![temp-tower](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Temp-calib/temp-tower.jpg?raw=true)
> [!NOTE]
> If a range of temperatures looks good, you may want to use the middle of that range as the optimal temperature.
> But if you are planning to print at higher [speeds](speed_settings_other_layers_speed)/[flow rates](volumetric-speed-calib), you may want to use the higher end of that range as the optimal temperature.
## Bed Temperature
Bed temperature plays a crucial role in ensuring proper filament adhesion to the build surface, which directly impacts both print success and quality.
Most materials have a relatively broad optimal range for bed temperature (typically +/-5°C).
In general, following the manufacturers recommendations, maintaining a clean bed (free from oils or fingerprints), ensuring a stable [chamber temperature](#chamber-temperature), and having a properly leveled bed will produce reliable results.
- If the bed temperature is too low, the filament may fail to adhere properly, leading to warping, weak layer bonding, or complete detachment. In severe cases, the printed part may dislodge entirely and stick to the nozzle or other printer components, potentially causing mechanical damage.
- If the bed temperature is too high, the lower layers can overheat and soften excessively, resulting in deformation such as [elephant foot](quality_settings_precision#elephant-foot-compensation).
> [!TIP]
> As a general guideline, you can use the [glass transition temperature](https://en.wikipedia.org/wiki/Glass_transition) (Tg) of the material and subtract 510°C to estimate a safe upper limit for bed temperature.
> See [this article](https://magigoo.com/blog/prevent-warping-temperature-and-first-layer-adhesion-magigoo/) for a detailed explanation.
> [!NOTE]
> For challenging prints involving materials with **high shrinkage** (e.g., nylons or polycarbonate) or geometries prone to warping, dialed-in settings are critical.
> In these cases, [chamber temperature](#chamber-temperature) becomes a **major factor** in preventing detachment and ensuring print success.
## Chamber Temperature
Chamber temperature can affect the print quality, especially for high-temperature filaments.
A heated chamber can help to maintain a consistent temperature throughout the print, reducing the risk of warping and improving layer adhesion. However, it is important to monitor the chamber temperature to ensure that it does not exceed the filament's deformation temperature.
See: [Chamber temperature printer settings](Chamber-temperature)
> [!IMPORTANT]
> Low temperature Filaments like PLA can clog the nozzle if the chamber temperature is too high.

View File

@@ -1,37 +0,0 @@
# Filament Tolerance Calibration
Each filament and printer combination can result in different tolerances. This means that even using the same filament and print profile, tolerances may vary from one printer to another.
To correct for these variations, OrcaSlicer provides:
- Filament Compensation:
- Shrinkage (XY)
![FilamentShrinkageCompensation](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/FilamentShrinkageCompensation.png?raw=true)
- Process Compensation:
- X-Y hole compensation
- X-Y contour compensation
- Precise wall
- Precise Z height
![QualityPrecision](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/QualityPrecision.png?raw=true)
## Handy Models
OrcaSlicer includes several handy models to help you test and calibrate your printer.
Right-click on your plate in Prepare mode and select "Add Handy Model" to access these models.
![handy-models-list](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Handy-Models/handy-models-list.png?raw=true)
### Orca Tolerance Test
This calibration test is designed to evaluate the dimensional accuracy of your printer and filament. The model consists of a base with six hexagonal holes, each with a different tolerance: 0.0 mm, 0.05 mm, 0.1 mm, 0.2 mm, 0.3 mm, and 0.4 mm, as well as a hexagon-shaped tester.
![tolerance_hole](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/tolerance_hole.svg?raw=true)
You can check the tolerance using either an M6 Allen key or the included printed hexagon tester.
Use calipers to measure both the holes and the inner tester. Based on your results, you can fine-tune the X-Y hole compensation and X-Y contour compensation settings. Repeat the process until you achieve the desired precision.
![OrcaToleranceTes_m6](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/OrcaToleranceTes_m6.jpg?raw=true)
![OrcaToleranceTest_print](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/Tolerance/OrcaToleranceTest_print.jpg?raw=true)

View File

@@ -1,22 +0,0 @@
# VFA
Vertical Fine Artifacts (VFA) are small surface imperfections that appear on vertical walls, especially near sharp corners or sudden directional changes. These artifacts are typically caused by mechanical vibrations, motor resonance, or rapid directional shifts that impact print quality.
- **Mechanical adjustments**, such as tuning or replacing motors, belts, or pulleys.
- **MMR (Motor Resonance Rippling)** is a common subtype of VFA caused by stepper motors vibrating at resonant frequencies, leading to periodic ripples on the surface.
- **[Jerk/Junction Deviation](cornering-calib)** settings can also contribute to VFA, as they control how the printer handles rapid changes in direction.
- **[Input Shaping](input-shaping-calib)** can help mitigate VFA by reducing vibrations during printing.
## VFA Test
The VFA Speed Test in OrcaSlicer helps identify which print speeds trigger MRR artifacts. It prints a vertical tower with walls at various angles while progressively increasing the print speed.
![vfa_test_menu](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/vfa/vfa_test_menu.png?raw=true)
![vfa_test_print](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/vfa/vfa_test_print.jpg?raw=true)
After printing, inspect the tower for MRR artifacts. Look for speeds where the surface becomes visibly smoother or rougher. This allows you to pinpoint problematic speed ranges.
You can then configure the **Resonance Avoidance Speed Range** in the printer profile to skip speeds that cause visible artifacts.
![vfa_resonance_avoidance](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/vfa/vfa_resonance_avoidance.png?raw=true)

View File

@@ -1,49 +0,0 @@
# Max Volumetric Speed (FlowRate) Calibration
Each material profile includes a **maximum volumetric speed** setting, which limits your [print speed](speed_settings_other_layers_speed) to prevent issues like nozzle clogs, under-extrusion, or poor layer adhesion.
This value varies depending on your **material**, **machine**, **nozzle diameter**, and even your **extruder setup**, so its important to calibrate it for your specific printer and each filament you use.
> [!NOTE]
> Even for the same material type (e.g., PLA), the **brand** and **color** can significantly affect the maximum flow rate.
> [!TIP]
> If you're planning to increase speed or flow, its a good idea to **increase your nozzle temperature**, preferably toward the higher end of the recommended range for your filament. Use a [temperature tower calibration](temp-calib#nozzle-temp-tower) to find that range.
## Calibration Overview
You will be prompted to enter the settings for the test: start volumetric speed, end volumetric speed, and step. It is recommended to use the default values (5mm³/s start, 20mm³/s end, with a step of 0.5), unless you already have an idea of the lower or upper limit for your filament. Select "OK", slice the plate, and send it to the printer.
Once printed, take note of where the layers begin to fail and where the quality begins to suffer.
> [!TIP]
> A **change in surface sheen** (glossy vs. matte) is often a visual cue of material degradation or poor layer adhesion.
![mvf_measurement_point](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/MVF/mvf_measurement_point.jpg?raw=true)
Use calipers or a ruler to measure the **height** of the model just before the defects begin.
![mvf_caliper_sample_mvf](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/MVF/mvf_caliper_sample_mvf.jpg?raw=true)
Then you can:
- Use the following formula
```math
Filament Max Volumetric Speed = start + (HeightMeasured * step)
```
In this case (19mm), so the calculation would be: `5 + (19 * 0.5) = 14.5mm³/s`
- Use OrcaSlicer in the "Preview" tab, make sure the color scheme "flow" is selected. Scroll down to the layer height that you measured, and click on the toolhead slider. This will indicate the max flow level for your filament.
![mvf_gui_flow](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/MVF/mvf_gui_flow.png?raw=true)
After you have determined the maximum volumetric speed, you can set it in the filament settings. This will ensure that the printer does not exceed the maximum flow rate for the filament.
![mvf_material_settings](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/MVF/mvf_material_settings.png?raw=true)
> [!NOTE]
> This test is a best case scenario and doesn't take into account Retraction or other settings that can increase clogs or under-extrusion.
> You may want to reduce the flow by 10%-20% (or even further) to ensure print quality/strength.
> **Printing at high volumetric speed can lead to poor layer adhesion or even clogs in the nozzle.**
> [!TIP]
> @ItsDeidara has made a html to help with the calculation. Check it out if those equations give you a headache [here](https://github.com/ItsDeidara/Orca-Slicer-Assistant).

View File

@@ -1,96 +0,0 @@
# Placeholders Variables
This section describes the general built-in placeholders variables available for use in G-code scripts and configurations.
- [Global Slicing State](#global-slicing-state)
- [Read Only](#read-only)
- [Read Write](#read-write)
- [Slicing State](#slicing-state)
- [Print Statistics](#print-statistics)
- [Objects Info](#objects-info)
- [Dimensions](#dimensions)
- [Temperatures](#temperatures)
- [Timestamps](#timestamps)
- [Presets](#presets)
## Global Slicing State
### Read Only
- **zhop**: Contains Z-hop present at the beginning of the custom G-code block.
### Read Write
- **e_position[]**: Current position of the extruder axis. Only used with absolute extruder addressing.
- **e_restart_extra[]**: Currently planned extra extruder priming after de-retraction.
- **e_retracted[]**: Retraction state at the beginning of the custom G-code block. If the custom G-code moves the extruder axis, it should write to this variable so OrcaSlicer knows where it travels from when it gets control back.
- **position[]**: Current position of the extruder axis. Only used with absolute extruder addressing.
## Slicing State
- **current_extruder**: Zero-based index of currently used extruder.
- **current_object_idx**: Zero-based index of currently printed object.
- **has_wipe_tower**: Whether or not wipe tower is being generated in the print.
- **initial_extruder**: Zero-based index of the first extruder used in the print. Same as initial_tool.
- **initial_tool**: Zero-based index of the first extruder used in the print. Same as initial_extruder.
- **is_extruder_used**: Vector of booleans stating whether a given extruder is used in the print.
- **has_single_extruder_multi_material_priming**: Whether or not single extruder multi-material priming is used in the print.
- **initial_no_support_extruder**: Zero-based index of the first extruder used for printing without support. Same as initial_no_support_tool.
- **in_head_wrap_detect_zone**: Indicates if the first layer overlaps with the head wrap zone.
## Print Statistics
- **extruded_volume**: Total filament volume extruded per extruder during the entire print.
- **extruded_volume_total**: Total volume of filament used during the entire print.
- **extruded_weight**: Total filament weight extruded per extruder during the entire print.
- **extruded_weight_total**: Total weight of filament used during the entire print.
- **total_print_time**: Total time taken for the print.
- **total_layer_count**: Total number of layers in the print.
## Objects Info
- **num_objects**: Total number of objects in the print.
- **num_instances**: Total number of object instances in the print, summed over all objects.
- **scale[]**: Contains a string with the information about what scaling was applied to the individual objects. Indexing of the objects is zero-based (first object has index 0).
- **input_filename_base**: Source filename of the first object, without extension.
- **input_filename**: Full input filename of the first object.
- **plate_name**: Name of the plate sliced.
## Dimensions
- **first_layer_print_convex_hull**: Vector of points of the first layer convex hull. Each element has the following format: '[x, y]' (x and y are floating-point numbers in mm).
- **first_layer_print_min**: Bottom-left corner of first layer bounding box.
- **first_layer_print_max**: Top-right corner of first layer bounding box.
- **first_layer_print_size**: Size of the first layer bounding box.
- **print_bed_min**: Bottom-left corner of print bed bounding box.
- **print_bed_max**: Top-right corner of print bed bounding box.
- **print_bed_size**: Size of the print bed bounding box.
- **first_layer_center_no_wipe_tower**: First layer center without wipe tower.
- **first_layer_height**: Height of the first layer.
## Temperatures
- **bed_temperature**: Vector of bed temperatures for each extruder/filament.
- **bed_temperature_initial_layer**: Vector of initial layer bed temperatures for each extruder/filament. Provides the same value as first_layer_bed_temperature.
- **bed_temperature_initial_layer_single**: Initial layer bed temperature for the initial extruder. Same as bed_temperature_initial_layer[initial_extruder].
- **chamber_temperature**: Vector of chamber temperatures for each extruder/filament.
- **overall_chamber_temperature**: Overall chamber temperature. This value is the maximum chamber temperature of any extruder/filament used.
- **first_layer_bed_temperature**: Vector of first layer bed temperatures for each extruder/filament. Provides the same value as bed_temperature_initial_layer.
- **first_layer_temperature**: Vector of first layer temperatures for each extruder/filament.
## Timestamps
- **timestamp**: String containing current time in yyyyMMdd-hhmmss format.
- **year**: Current year.
- **month**: Current month.
- **day**: Current day.
- **hour**: Current hour.
- **minute**: Current minute.
- **second**: Current second.
## Presets
Each preset's ([Print process settings](home#process-settings), [Filament/Material settings](home#material-settings), [Printer settings](home#printer-settings)) variables can be used in your G-code scripts and configurations.
> [!TIP]
> To know the variable name you can hover your mouse over the variable in the UI.

View File

@@ -1,340 +0,0 @@
# How to Build
This wiki page provides detailed instructions for building OrcaSlicer from source on different operating systems, including Windows, macOS, and Linux.
It includes tool requirements, setup commands, and build steps for each platform.
Whether you're a contributor or just want a custom build, this guide will help you compile OrcaSlicer successfully.
- [Windows 64-bit](#windows-64-bit)
- [Windows Tools Required](#windows-tools-required)
- [Windows Instructions](#windows-instructions)
- [MacOS 64-bit](#macos-64-bit)
- [MacOS Tools Required](#macos-tools-required)
- [MacOS Instructions](#macos-instructions)
- [Debugging in Xcode](#debugging-in-xcode)
- [Linux](#linux)
- [Using Docker](#using-docker)
- [Docker Dependencies](#docker-dependencies)
- [Docker Instructions](#docker-instructions)
- [Troubleshooting](#troubleshooting)
- [Linux Build](#linux-build)
- [Dependencies](#dependencies)
- [Common dependencies across distributions](#common-dependencies-across-distributions)
- [Additional dependencies for specific distributions](#additional-dependencies-for-specific-distributions)
- [Linux Instructions](#linux-instructions)
- [Portable User Configuration](#portable-user-configuration)
- [Example folder structure](#example-folder-structure)
## Windows 64-bit
How to building with Visual Studio 2022 on Windows 64-bit.
### Windows Tools Required
- [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or Visual Studio 2019
```shell
winget install --id=Microsoft.VisualStudio.2022.Professional -e
```
- [CMake (version 3.31)](https://cmake.org/) — **⚠️ version 3.31.x is mandatory**
```shell
winget install --id=Kitware.CMake -v "3.31.6" -e
```
- [Strawberry Perl](https://strawberryperl.com/)
```shell
winget install --id=StrawberryPerl.StrawberryPerl -e
```
- [Git](https://git-scm.com/)
```shell
winget install --id=Git.Git -e
```
- [git-lfs](https://git-lfs.com/)
```shell
winget install --id=GitHub.GitLFS -e
```
> [!TIP]
> GitHub Desktop (optional): A GUI for Git and Git LFS, which already includes both tools.
> ```shell
> winget install --id=GitHub.GitHubDesktop -e
> ```
### Windows Instructions
1. Clone the repository:
- If using GitHub Desktop clone the repository from the GUI.
- If using the command line:
1. Clone the repository:
```shell
git clone https://github.com/SoftFever/OrcaSlicer
```
2. Run lfs to download tools on Windows:
```shell
git lfs pull
```
2. Open the appropriate command prompt:
- For Visual Studio 2019:
Open **x64 Native Tools Command Prompt for VS 2019** and run:
```shell
build_release.bat
```
- For Visual Studio 2022:
Open **x64 Native Tools Command Prompt for VS 2022** and run:
```shell
build_release_vs2022.bat
```
> [!NOTE]
> The build process will take a long time depending on your system but even with high-end hardware it can take up to 40 minutes.
> [!TIP]
> If you encounter issues, you can try to uninstall ZLIB from your Vcpkg library.
3. If successful, you will find the Visual Studio solution file in:
```shell
build\OrcaSlicer.sln
```
4. Open the solution in Visual Studio, set the build configuration to `Release` and run the `Local Windows Debugger`.
![compile_vs2022_local_debugger](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/develop/compile_vs2022_local_debugger.png?raw=true)
5. Your resulting executable will be located in:
```shell
\build\src\Release\orca-slicer.exe
```
> [!NOTE]
> The first time you build a branch, it will take a long time.
> Changes to .cpp files are quickly compiled.
> Changes to .hpp files take longer, depending on what you change.
> If you switch back and forth between branches, it also takes a long time to rebuild, even if you haven't made any changes.
> [!IMPORTANT]
> Make sure that CMake version 3.31.x is actually being used. Run `cmake --version` and verify it returns a **3.31.x** version.
> If you see an older version (e.g. 3.29), it's likely due to another copy in your system's PATH (e.g. from Strawberry Perl).
> You can run where cmake to check the active paths and rearrange your **System Environment Variables** > PATH, ensuring the correct CMake (e.g. C:\Program Files\CMake\bin) appears before others like C:\Strawberry\c\bin.
> [!TIP]
> If the build fails, try deleting the `build/` and `deps/build/` directories to clear any cached build data. Rebuilding after a clean-up is usually sufficient to resolve most issues.
## MacOS 64-bit
How to building with Xcode on MacOS 64-bit.
### MacOS Tools Required
- Xcode
- CMake (version 3.31.x is mandatory)
- Git
- gettext
- libtool
- automake
- autoconf
- texinfo
> [!TIP]
> You can install most of them by running:
> ```shell
> brew install gettext libtool automake autoconf texinfo
> ```
Homebrew currently only offers the latest version of CMake (e.g. **4.X**), which is not compatible. To install the required version **3.31.X**, follow these steps:
1. Download CMake **3.31.7** from: [https://cmake.org/download/](https://cmake.org/download/)
2. Install the application (drag it to `/Applications`).
3. Add the following line to your shell configuration file (`~/.zshrc` or `~/.bash_profile`):
```sh
export PATH="/Applications/CMake.app/Contents/bin:$PATH"
```
4. Restart the terminal and check the version:
```sh
cmake --version
```
5. Make sure it reports a **3.31.x** version.
> [!IMPORTANT]
> If you've recently upgraded Xcode, be sure to open Xcode at least once and install the required macOS build support.
### MacOS Instructions
1. Clone the repository:
```shell
git clone https://github.com/SoftFever/OrcaSlicer
cd OrcaSlicer
```
2. Build the application:
```shell
./build_release_macos.sh
```
3. Open the application:
```shell
open build/arm64/OrcaSlicer/OrcaSlicer.app
```
### Debugging in Xcode
To build and debug directly in Xcode:
1. Open the Xcode project:
```shell
open build/arm64/OrcaSlicer.xcodeproj
```
2. In the menu bar:
- **Product > Scheme > OrcaSlicer**
- **Product > Scheme > Edit Scheme...**
- Under **Run > Info**, set **Build Configuration** to `RelWithDebInfo`
- Under **Run > Options**, uncheck **Allow debugging when browsing versions**
- **Product > Run**
## Linux
Linux distributions are available in two formats: [using Docker](#using-docker) (recommended) or [building directly](#linux-build) on your system.
### Using Docker
How to build and run OrcaSlicer using Docker.
#### Docker Dependencies
- Docker
- Git
#### Docker Instructions
```shell
git clone https://github.com/SoftFever/OrcaSlicer && cd OrcaSlicer && ./scripts/DockerBuild.sh && ./scripts/DockerRun.sh
```
### Troubleshooting
The `scripts/DockerRun.sh` script includes several commented-out options that can help resolve common issues. Here's a breakdown of what they do:
- `xhost +local:docker`: If you encounter an "Authorization required, but no authorization protocol specified" error, run this command in your terminal before executing `scripts/DockerRun.sh`. This grants Docker containers permission to interact with your X display server.
- `-h $HOSTNAME`: Forces the container's hostname to match your workstation's hostname. This can be useful in certain network configurations.
- `-v /tmp/.X11-unix:/tmp/.X11-unix`: Helps resolve problems with the X display by mounting the X11 Unix socket into the container.
- `--net=host`: Uses the host's network stack, which is beneficial for printer Wi-Fi connectivity and D-Bus communication.
- `--ipc host`: Addresses potential permission issues with X installations that prevent communication with shared memory sockets.
- `-u $USER`: Runs the container as your workstation's username, helping to maintain consistent file permissions.
- `-v $HOME:/home/$USER`: Mounts your home directory into the container, allowing you to easily load and save files.
- `-e DISPLAY=$DISPLAY`: Passes your X display number to the container, enabling the graphical interface.
- `--privileged=true`: Grants the container elevated privileges, which may be necessary for libGL and D-Bus functionalities.
- `-ti`: Attaches a TTY to the container, enabling command-line interaction with OrcaSlicer.
- `--rm`: Automatically removes the container once it exits, keeping your system clean.
- `orcaslicer $*`: Passes any additional parameters from the `scripts/DockerRun.sh` script directly to the OrcaSlicer executable within the container.
By uncommenting and using these options as needed, you can often resolve issues related to display authorization, networking, and file permissions.
### Linux Build
How to build OrcaSlicer on Linux.
#### Dependencies
The build system supports multiple Linux distributions including Ubuntu/Debian and Arch Linux. All required dependencies will be installed automatically by the provided shell script where possible, however you may need to manually install some dependencies.
> [!NOTE]
> Fedora and other distributions are not currently supported, but you can try building manually by installing the required dependencies listed below.
##### Common dependencies across distributions
- autoconf / automake
- cmake
- curl / libcurl4-openssl-dev
- dbus-devel / libdbus-1-dev
- eglexternalplatform-dev / eglexternalplatform-devel
- extra-cmake-modules
- file
- gettext
- git
- glew-devel / libglew-dev
- gstreamer-devel / libgstreamerd-3-dev
- gtk3-devel / libgtk-3-dev
- libmspack-dev / libmspack-devel
- libsecret-devel / libsecret-1-dev
- libspnav-dev / libspnav-devel
- libssl-dev / openssl-devel
- libtool
- libudev-dev
- mesa-libGLU-devel
- ninja-build
- texinfo
- webkit2gtk-devel / libwebkit2gtk-4.0-dev or libwebkit2gtk-4.1-dev
- wget
##### Additional dependencies for specific distributions
- **Ubuntu 22.x/23.x**: libfuse-dev, m4
- **Arch Linux**: mesa, wayland-protocols
#### Linux Instructions
1. **Install system dependencies:**
```shell
./build_linux.sh -u
```
2. **Build dependencies:**
```shell
./build_linux.sh -d
```
3. **Build OrcaSlicer with tests:**
```shell
./build_linux.sh -st
```
4. **Build AppImage (optional):**
```shell
./build_linux.sh -i
```
5. **All-in-one build (recommended):**
```shell
./build_linux.sh -dsti
```
**Additional build options:**
- `-b`: Build in debug mode (mostly broken at runtime for a long time; avoid unless you want to be fixing failed assertions)
- `-c`: Force a clean build
- `-C`: Enable ANSI-colored compile output (GNU/Clang only)
- `-e`: Build RelWithDebInfo (release + symbols)
- `-j N`: Limit builds to N cores (useful for low-memory systems)
- `-1`: Limit builds to one core
- `-l`: Use Clang instead of GCC
- `-p`: Disable precompiled headers (boost ccache hit rate)
- `-r`: Skip RAM and disk checks (for low-memory systems)
> [!NOTE]
> The build script automatically detects your Linux distribution and uses the appropriate package manager (apt, pacman) to install dependencies.
> [!TIP]
> For first-time builds, use `./build_linux.sh -u` to install dependencies, then `./build_linux.sh -dsti` to build everything.
> [!WARNING]
> If you encounter memory issues during compilation, use `-j 1` or `-1` to limit parallel compilation and `-r` to skip memory checks.
#### Unit Testing
See [How to Test](How-to-test) for more details.
---
## Portable User Configuration
If you want OrcaSlicer to use a custom user configuration folder (e.g., for a portable installation), you can simply place a folder named `data_dir` next to the OrcaSlicer executable. OrcaSlicer will automatically use this folder as its configuration directory.
This allows for multiple self-contained installations with separate user data.
> [!TIP]
> This feature is especially useful if you want to run OrcaSlicer from a USB stick or keep different profiles isolated.
### Example folder structure
```shell
OrcaSlicer.exe
data_dir/
```
You dont need to recompile or modify any settings — this works out of the box as long as `data_dir` exists in the same folder as the executable.

View File

@@ -1,434 +0,0 @@
# Guide: Develop Profiles for OrcaSlicer
## Introduction
This guide will help you develop profiles for OrcaSlicer.
## High-level Overview
OrcaSlicer uses JSON files to store profiles. There are four types of profiles:
1. Printer model (type `machine_model`). Example: `Orca 3D Fuse1.json`
2. Printer variant (type `machine`). Example: `Orca 3D Fuse1 0.2 nozzle.json`
3. Filament (type `filament`). Example: `Generic PLA @Orca 3D Fuse1@.json`
4. Process (type `process`). Example: `0.10mm Standard @Orca 3D Fuse1 0.2.json`
Additionally, there is an overall meta file for each vendor (`Orca 3D.json`).
For easier understanding, let's consider a scenario with a printer manufacturer called `Orca 3D`. The manufacturer offers one printer model called `Fuse 1`, which supports 0.2/0.4/0.6/0.8mm nozzles and common market filaments.
In this case:
- Vendor profile: `Orca 3D`
- Printer profile: `Orca 3D Fuse1`
- Printer variant profile: `Orca 3D Fuse1 0.4 nozzle`
- Filament profile: `Generic PLA @Orca 3D Fuse1@`
- Process profile: `0.20mm Standard @Orca 3D Fuse1 0.4`
The profile name should be same as the filename without the `.json` extension in principal.
Naming conventions:
1. Vendor profile: `vendor_name.json`
2. Printer profile: `vendor_name` + `printer_name` + `.json`
3. Printer variant profile: `vendor_name` + `printer_variant_name` + `.json` (where `printer_variant_name` typically includes `printer_name` + `nozzle_diameter`)
4. Filament profile: `filament_vendor_name` + `filament_name` + " @" + `vendor_name` + `printer_name`/`printer_variant_name` + `.json`
5. Process profile: `layer_height` + `preset_name` + " @" + `vendor_name` + `printer_name`/`printer_variant_name` + `.json` (`preset_name` typically includes "standard," "fine," "fast," "draft," etc.)
## File Structure and Templates
Profiles should be structured in the following way under the OrcaSlicer installation directory:
```plaintext
resources\profiles\
├── Orca 3D.json
└── Orca 3D\
├── machine\
│ ├── Orca 3D Fuse1.json
│ ├── Orca 3D Fuse1 0.2 nozzle.json
│ └── Orca 3D Fuse1 0.4 nozzle.json
├── process\
│ ├── 0.10mm Standard @Orca 3D Fuse1 0.2.json
│ └── 0.20mm Standard @Orca 3D Fuse1 0.4.json
└── filament\
└── Generic PLA @Orca 3D Fuse1@.json
```
> [!TIP]
> Use short vendor names in filenames to avoid excessive length.
> [!NOTE]
> Filament profiles are **optional**. Create them only if the vendor has specifically tuned profiles for the given printer. See [Filament profiles](#filament-profiles) for details.
Template files for profiles are available in:
```shell
OrcaSlicer\resources\profiles_template\Template
```
These templates can be used as a starting point for new printer, filament, and process profiles.
## Filament Profiles
OrcaSlicer features a global filament library called `OrcaFilamentLibrary`, which is automatically available for all printers. It includes generic filaments like `Generic PLA @System` and `Generic ABS @System` etc.
Printer vendors can override specific filaments in the global library for certain printer models by creating new filament profiles.
Relationship diagram:
```mermaid
graph TD;
OrcaFilamentLibrary-->Orca_3D_filament;
OrcaFilamentLibrary-->Vendor_A_filament;
OrcaFilamentLibrary-->Vendor_B_filament;
```
> [!IMPORTANT]
> Create new filament profiles only if you have truly specifically tuned the filament for the given printer. Otherwise, use the global library. The global library has a better chance to receive optimizations and updates from OrcaSlicer contributors, which will benefit users of all printers.
### Adding Filament Profiles to the Global Library
In this section, we will discuss how to add a new filament profile into the global library.
If you want to add a new generic profile into the global library, you need to create a new file in the `resources\profiles\OrcaFilamentLibrary\filament` folder. If a base type already exists in the global library, you can use this file as a base profile by inheriting it.
The following sample JSON file shows how to create a new generic filament profile `Generic PLA-GF @System` in the global library.
1. The first step is to create a new file in the `resources\profiles\OrcaFilamentLibrary\filament` folder. The file name should be `Generic PLA-GF @System.json`. Please note that we leave the `compatible_printers` field empty so that it is available for all printers.
```json
{
"type": "filament",
"filament_id": "GFL99",
"setting_id": "GFSA05",
"name": "Generic PLA-GF @System",
"from": "system",
"instantiation": "true",
"inherits": "fdm_filament_pla",
"filament_type": ["PLA-GF"],
"filament_flow_ratio": [
"0.96"
],
"compatible_printers": []
}
```
2. Register the profile in `resources\profiles\OrcaFilamentLibrary.json`:
```json
{
"name": "OrcaFilamentLibrary",
"version": "02.02.00.04",
"force_update": "0",
"description": "Orca Filament Library",
"filament_list": [
// ...
{
"name": "Generic PLA-GF @System",
"sub_path": "filament/Generic PLA-GF @System.json"
}
]
}
```
3. The last step is to validate the newly added filament profiles see [Validate Profiles](#validate-profiles).
> [!NOTE]
> If the filament is compatible with AMS, ensure that the `filament_id` value **does not exceed 8 characters** to maintain AMS compatibility.
> [!TIP]
> **Testing Profile Changes**
>
> When developing profiles, you may notice that changes aren't reflected in OrcaSlicer after editing profile files. This happens because OrcaSlicer caches profiles in the system folder.
> To force OrcaSlicer to load your updated profiles:
> 1. **Access the configuration folder**: Go to **Help** → **Show Configuration Folder**
> ![go-to-configuration-folder](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/develop/go-to-configuration-folder.png?raw=true)
> 2. **Clear the cache**: Delete the `system` folder to remove cached profiles
> ![profile-delete-system-folder](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/develop/profile-delete-system-folder.png?raw=true)
> 3. **Restart OrcaSlicer**: Launch the application to load your updated profiles
> This process forces OrcaSlicer to update its profile cache from the source files in the `resources/profiles/` directory.
### Adding Filament Profiles to Printer Vendor Library
In this section, we will discuss how to add a new filament profile for a certain vendor.
If you want to add a new filament profile, whether it's a brand new profile or a specialized version of a global filament profile for a given printer, you need to create a new file in the `resources\profiles\vendor_name\filament` folder. If a base type already exists in the global library, you can use this file as a base profile by inheriting it.
Below is a sample JSON file showing how to create a specialized `Generic ABS` filament profile for the ToolChanger printer.
Please note that here we must leave the compatible_printers field non-empty, unlike in the global library.
```json
{
"type": "filament",
"setting_id": "GFB99_MTC_0",
"name": "Generic ABS @MyToolChanger",
"from": "system",
"instantiation": "true",
"inherits": "Generic ABS @System",
"filament_cooling_final_speed": [
"3.5"
],
"filament_cooling_initial_speed": [
"10"
],
"filament_cooling_moves": [
"2"
],
"filament_load_time": [
"10.5"
],
"filament_loading_speed": [
"10"
],
"filament_loading_speed_start": [
"50"
],
"filament_multitool_ramming": [
"1"
],
"filament_multitool_ramming_flow": [
"40"
],
"filament_stamping_distance": [
"45"
],
"filament_stamping_loading_speed": [
"29"
],
"filament_unload_time": [
"8.5"
],
"filament_unloading_speed": [
"100"
],
"compatible_printers": [
"MyToolChanger 0.4 nozzle",
"MyToolChanger 0.2 nozzle",
"MyToolChanger 0.6 nozzle",
"MyToolChanger 0.8 nozzle"
]
}
```
> [!NOTE]
> If the filament is compatible with AMS, ensure that the `filament_id` value **does not exceed 8 characters** to maintain AMS compatibility.
## Process Profiles
Process profiles define print quality and behavior. They follow a structure similar to filament profiles:
- A common base file, e.g., `fdm_process_common.json`, acts as the parent.
- Vendor-specific process profiles should inherit from the base using the `inherits` field.
- Profiles are stored under:
```shell
resources\profiles\vendor_name\process\
```
- **There are no global process profiles**.
- Each process profile includes a `"compatible_printers"` field with an array of compatible printer variant names.
Example:
```json
{
"type": "process",
"name": "0.10mm Standard @ExampleVendor Printer 0.2",
"inherits": "fdm_process_common",
"from": "system",
"instantiation": "true",
"compatible_printers": [
"ExampleVendor Printer 0.2 nozzle"
]
}
```
## Printer Model Profiles
- Printer model profiles (type `machine_model`) describe the general printer information.
- Example fields: `nozzle_diameter`, `bed_model`, `bed_texture`, `model_id`, etc.
- Stored in:
```shell
resources\profiles\vendor_name\machine\
```
- Each vendor's folder may contain an image named:
```shell
[machine_model_list.name]_cover.png
```
This image will be used in the UI.
Example model profile:
```json
{
"type": "machine_model",
"name": "Example M5",
"nozzle_diameter": "0.2;0.25;0.4;0.6",
"bed_model": "M5-Example-bed.stl",
"bed_texture": "M5-Example-texture.svg",
"model_id": "V1234",
"family": "Example",
"machine_tech": "FFF",
"default_materials": "Example Generic PLA;Example Generic PETG"
}
```
## Printer Variant Profiles
- Printer variants (type `machine`) define specific nozzle configurations and mechanical details.
- Each variant must inherit from a common base like `fdm_machine_common.json`.
- Must list the compatible nozzle diameter in the `nozzle_diameter` array.
- Example fields include `printer_model`, `printer_variant`, `default_print_profile`, `printable_area`, etc.
Example variant profile:
```json
{
"type": "machine",
"name": "Example M5 0.2 nozzle",
"inherits": "fdm_machine_common",
"from": "system",
"setting_id": "GM001",
"instantiation": "true",
"nozzle_diameter": ["0.2"],
"printer_model": "Example M5",
"printer_variant": "0.2",
"default_filament_profile": ["Example Generic PLA"],
"default_print_profile": "0.10mm Standard 0.2mm nozzle @Example",
"printable_area": ["0x0", "235x0", "235x235", "0x235"],
"nozzle_type": "brass"
}
```
## Models
- The `model` directory under the vendor folder is intended to behave similarly to `machine` profiles.
- Used for additional printer-related 3D models or definitions, stored at:
```shell
resources\profiles\vendor_name\model\
```
## Vendor Meta File
```shell
resources\profiles\vendor_name.json
```
Each vendor must include a JSON file in the `resources\profiles` directory, named `vendor_name.json`. This file lists all available models, variants, processes, and filaments:
Example:
```json
{
"name": "ExampleVendor",
"version": "01.00.00.00",
"force_update": "1",
"description": "Example configuration",
"machine_model_list": [
{
"name": "Example M5",
"sub_path": "machine/Example M5.json"
}
],
"machine_list": [
{
"name": "fdm_machine_common",
"sub_path": "machine/fdm_machine_common.json"
}
],
"process_list": [
{
"name": "fdm_process_common",
"sub_path": "process/fdm_process_common.json"
}
],
"filament_list": [
{
"name": "fdm_filament_common",
"sub_path": "filament/fdm_filament_common.json"
}
]
}
```
## Validate Profiles
You can validate your profiles using both the **OrcaSlicer profile validator** and the **Python validation script**. These tools are designed to check different aspects of the profiles, so both should be executed and pass without errors to ensure full compatibility.
> [!NOTE]
> **✅ Recommendation:** Always run **both** the OrcaSlicer validator and the Python script to ensure all aspects of the profiles are valid.
### 1. OrcaSlicer Profile Validator
You can run OrcaSlicer to verify if the filament you just added is available and usable. You can also use the [Orca profile validator](https://github.com/SoftFever/Orca_tools/releases/tag/1) tool to help debug any errors.
> [!IMPORTANT]
> You need to delete the `%appdata%/OrcaSlicer/system` folder to force OrcaSlicer to reload your lastest changes.
The process is the same if you want to add a new brand filament profile into the global library. You need to create a new file in the `resources\profiles\OrcaFilamentLibrary\filament\brand_name` folder. The only difference is that you should put the file into the brand's own subfolder.
#### Usage
```shell
-h [ --help ] help
-p [ --path ] arg profile folder
-v [ --vendor ] arg Vendor name. Optional, all profiles present in the folder will be validated if not specified
-l [ --log_level ] arg (=2) Log level. Optional, default is 2 (warning). Higher values produce more detailed logs.
```
#### Example
```shell
./OrcaSlicer_profile_validator -p ~/codes/OrcaSlicer/resources/profiles -l 2 -v Custom
```
#### Sample result with errors
```shell
PS D:\codes\OrcaSlicer> ."D:/codes/OrcaSlicer/build/src/Release/OrcaSlicer_profile_validator.exe" --path d:\codes\OrcaSlicer\resources\profiles -l 2 -v Custom
[2024-02-28 21:23:06.102138] [0x0000a4e8] [error] Slic3r::ConfigBase::load_from_json: parse d:\codes\OrcaSlicer\resources\profiles/Custom/machine/fdm_klipper_common.json got a nlohmann::detail::parse_error, reason = [json.exception.parse_error.101] parse error at line 9, column 38: syntax error while parsing object - unexpected string literal; expected '}'
...
Validation failed
```
#### Sample result with success
```shell
PS D:\codes\OrcaSlicer\build\src\RelWithDebInfo> ."D:/codes/OrcaSlicer/build/src/Release/OrcaSlicer_profile_validator.exe" --path d:\codes\OrcaSlicer\resources\profiles -l 2 -v Custom
Validation completed successfully
```
> [!WARNING]
> Use `OrcaSlicer_profile_validator` on Ubuntu and `OrcaSlicer_profile_validator.exe` on Windows.
---
### 2. Python Profile Validation Script
In addition to the Orca validator, you should run the `orca_extra_profile_check.py` script. This script performs additional checks like:
- Validation of `compatible_printers` in filament profiles
- Consistency of filament names
- Validation of default materials in machine profiles (optional)
#### Example command
```shell
python ./orca_extra_profile_check.py
```
You can also enable or disable specific checks:
- `--help`: displays help information
- `--vendor` (optional): checks only the specified vendor. If omitted, all vendors are checked.
- `--check-filaments` (enabled by default): checks `compatible_printers` fields in filament profiles
- `--check-materials`: checks default material names in machine profiles
- `--check-obsolete-keys`: checks for obsolete keys in profiles
#### Sample usage with all checks enabled
```shell
python ./orca_extra_profile_check.py --vendor="vendor_name" --check-filaments --check-materials
```
The script will output the number of errors found and exit with a non-zero status code if any issues are detected.

View File

@@ -1,27 +0,0 @@
# How to Test
This wiki page describes how to build and run tests on Linux. It should eventually provide guidance on how to add tests for a new feature.
## Build Tests
Can be built when you are building Orca Slicer binary by including the `-t` flag for `build_linux.sh`:
```
build_linux.sh -st
```
Test binaries will then appear under `build/tests`. As of this writing, not all tests will be built.
## Run Unit Tests
### Run All
```
ctest --test-dir build/tests
```
### Run a Specific Set
```
ctest --test-dir build/tests/slic3rutils
```

View File

@@ -1,266 +0,0 @@
# How to Contribute to the Wiki
This guide explains how to contribute to the OrcaSlicer wiki.
OrcaSlicer uses GitHub's wiki feature, which lets users and developers create and edit documentation collaboratively.
We encourage developers and users to contribute by updating existing pages and adding new content. This helps keep the documentation accurate and useful.
When adding new features, consider updating the wiki so users can access the latest guidance.
- [Wiki Structure](#wiki-structure)
- [Home](#home)
- [Index and Navigation](#index-and-navigation)
- [File Naming and Organization](#file-naming-and-organization)
- [Orca to Wiki Redirection](#orca-to-wiki-redirection)
- [Formatting and Style](#formatting-and-style)
- [Markdown Formatting](#markdown-formatting)
- [Alerts and Callouts](#alerts-and-callouts)
- [Images](#images)
- [Image Naming](#image-naming)
- [Image Placement](#image-placement)
- [Linking Images](#linking-images)
- [Examples](#examples)
- [Avoid the Following](#avoid-the-following)
- [Resize Images](#resize-images)
- [Image Cropping and Highlighting](#image-cropping-and-highlighting)
- [Recommended Formats](#recommended-formats)
- [Structuring Content](#structuring-content)
- [Commands and Code Blocks](#commands-and-code-blocks)
- [External Links](#external-links)
## Wiki Structure
Each wiki page is a Markdown file located in the `doc` directory of the repository. The wiki is organized into sections that cover different areas of the project.
### Home
The Home page is the starting point for the OrcaSlicer wiki. From there you can navigate to sections and topics related to the project.
When you create a new page or section, link it from the Home page under the appropriate category.
The Home page currently organizes content in these top-level entries:
- ![printer](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/printer.svg?raw=true) [Printer Settings](home#printer-settings)
- ![filament](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/filament.svg?raw=true) [Material Settings](home#material-settings)
- ![process](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/process.svg?raw=true) [Process Settings](home#process-settings)
- ![tab_3d_active](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/tab_3d_active.svg?raw=true) [Prepare](home#prepare)
- ![tab_calibration_active](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/tab_calibration_active.svg?raw=true) [Calibrations](home#calibrations)
- ![im_code](https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/im_code.svg?raw=true) [Developer Section](home#developer-section)
Each section can have multiple pages covering specific topics. For example, the [Process Settings](home#process-settings) section includes pages on [quality](home#quality-settings), [support](home#support-settings), and [others](home#others-settings).
#### Index and Navigation
GitHub Wiki uses file names as page identifiers. To link to a page, use the file name without the `.md` extension. If a file lives in a subdirectory, **do not include the subdirectory** in the link; link directly to the file name from the Home page.
For example, if you add `doc/calibration/flow-rate-calib.md`, link it like this:
```markdown
[Flow Rate Calibration](flow-rate-calib)
```
For long pages, include a table of contents at the top to help readers find sections quickly.
```markdown
- [Wiki Structure](#wiki-structure)
- [Home](#home)
- [Index and Navigation](#index-and-navigation)
- [File Naming and Organization](#file-naming-and-organization)
- [Formatting and Style](#formatting-and-style)
```
> [!NOTE]
> If you're adding a new section, follow the existing structure and make sure it doesn't already fit an existing category. Link it from the Home page accordingly.
### File Naming and Organization
When creating new pages, follow these file-naming conventions:
- Use unique file names to avoid conflicts.
- Use descriptive names that reflect the page's content.
- Use kebab-case for filenames (e.g.: `How-to-wiki.md`).
- If a page belongs to a section, include a suffix that clarifies it (for example, calibration pages should end with `-calib.md`, e.g. `flow-rate-calib.md`).
- Place files in the appropriate subdirectory when applicable (e.g.: `doc/calibration/` for calibration-related content).
## Orca to Wiki Redirection
OrcaSlicer can redirect users from the GUI to the appropriate wiki pages, making it easier to find relevant documentation.
The option-to-wiki mapping is defined in [src/slic3r/GUI/Tab.cpp](https://github.com/SoftFever/OrcaSlicer/blob/main/src/slic3r/GUI/Tab.cpp). Any option added with `append_single_option_line` can be mapped to a wiki page using a second string argument.
```cpp
optgroup->append_single_option_line("OPTION_NAME"); // Option without wiki page/redirection
optgroup->append_single_option_line("OPTION_NAME", "WIKI_PAGE"); // Option with wiki page and redirection
```
You can also point to a specific section within a wiki page by appending a fragment identifier (for example `#section-name`).
Example:
```cpp
optgroup->append_single_option_line("seam_gap","quality_settings_seam"); // Wiki page and redirection
optgroup->append_single_option_line("seam_slope_type", "quality_settings_seam#scarf-joint-seam"); // Wiki page and redirection to `Scarf Joint Seam` section
```
## Formatting and Style
Follow these style and formatting conventions when contributing to the wiki.
### Markdown Formatting
The wiki uses standard Markdown syntax for formatting and aims to maintain a consistent style across all pages. Avoid using raw HTML tags and prefer Markdown formatting instead.
Ensure your indentation is consistent, especially for code blocks and lists.
Refer to the [GitHub Markdown Guide](https://guides.github.com/features/mastering-markdown/) for more information on Markdown syntax.
### Alerts and Callouts
Use GitHub's alert syntax to add inline notes and warnings:
```markdown
> [!NOTE]
> Useful information that readers should know.
> [!TIP]
> Helpful advice for doing things more easily.
> [!IMPORTANT]
> Key information required to achieve a goal.
> [!WARNING]
> Urgent information to avoid problems.
> [!CAUTION]
> Warnings about risks or negative outcomes.
```
> [!NOTE]
> Refer to the [GitHub Alerts documentation](https://docs.github.com/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) for more details.
## Images
Images are encouraged to enhance the clarity and quality of the wiki content. They help illustrate concepts, provide examples, and improve readability.
> [!CAUTION]
> Do not use images from third-party sources unless you have the proper permissions.
### Image Naming
- Use clear, descriptive filenames that reflect the image content.
- For section-specific images, include the section name or initials (for example `pa-[description].png` for Pressure Advance images).
### Image Placement
- General images should be placed in the `doc/images/` directory.
- Section-specific images should be stored in their corresponding subdirectories (e.g., `doc/images/calibration/` for calibration content).
> [!TIP]
> You can use `\resources\images` images used in the GUI.
### Linking Images
Always use raw GitHub URLs for image links to ensure correct display:
Format = `![`filename`](` + Base URL + filename.extension + Raw tag + `)`
- Base URL:
```markdown
https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/
```
- Raw tag:
```markdown
?raw=true
```
#### Examples
- For an image in `doc/images/` named `calibration.png`:
```markdown
![calibration](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/calibration.png?raw=true)
```
- For an image in a subdirectory like `doc/images/GUI/combobox.png`:
```markdown
![combobox](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/combobox.png?raw=true)
```
> [!IMPORTANT]
> New or moved images may not appear in previews until the pull request is merged. Double-check paths and update links if you move files.
#### Avoid the Following
- Relative paths
- GitHub Assets/user-content/user-images URLs
- External image links from temporary or unreliable hosts
- Images containing personal or sensitive information
- Using images for content that can be expressed in text, such as equations or code—use Markdown syntax or Mermaid/Math formatting instead.
> [!NOTE]
> When contributing section-specific images, follow the naming conventions and directory structure.
#### Resize Images
Avoid the resize of images and let the Wiki handle it automatically.
If resizing is necessary (e.g., for thumbnails), use the following syntax:
HTML Format = `<img alt="` + filename + `"` + `src="` + Base URL + filename.extension + Raw tag + size limit.
Example:
```html
<img alt="IS_damp_marlin_print_measure" src="https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/InputShaping/IS_damp_marlin_print_measure.jpg?raw=true" height="200">
```
### Image Cropping and Highlighting
To ensure clarity:
- Crop images to focus on relevant areas.
- Use simple annotations (arrows, circles, rectangles) to highlight important parts without overloading the image.
### Recommended Formats
- **JPG:** Suitable for photographs. Avoid for images with text or fine detail due to compression artifacts.
- **PNG:** Ideal for screenshots or images with transparency. Ensure sufficient contrast for light and dark modes.
- **SVG:** Preferred when possible. SVGs support theme adaptation (light/dark mode), making them ideal for icons and diagrams.
## Structuring Content
Each page should have a clear objective. After a short introduction, choose a structure that fits the content:
- **Step-by-step guides:** Use for sequential procedures (for example calibration).
- **GUI-based reference:** Describe settings following OrcaSlicer's UI when sequence isn't required.
Example: explain **Layer Height** before **Initial Layer Height**, since the former is global and the latter only applies to the first layer.
## Commands and Code Blocks
When adding commands or code blocks please use the [Code Block with Syntax Highlighting feature of Markdown](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting).
- Use triple backticks (```) to enclose code blocks.
- Specify the language for proper highlighting and readability.
````markdown
```json
{
"key": "value"
}
```
````
```json
{
"key": "value"
}
```
## External Links
Be careful when linking to external resources.
Ensure links are relevant and reliable and cite papers or articles when appropriate.

View File

@@ -1,111 +0,0 @@
# Localization and translation guide
The purpose of this guide is to describe how to contribute to the OrcaSlicer translations. We use GNUgettext for extracting string resources from the project and PoEdit for editing translations.
Those can be downloaded here:
- [GNUgettext](https://www.gnu.org/software/gettext/) package contains a set of tools to extract strings from the source code and to create the translation Catalog.
- [PoEdit](https://poedit.net) provides good interface for the translators.
After GNUgettext is installed, it is recommended to add the path to gettext/bin to PATH variable.
Full manual for GNUgettext can be seen here: [http://www.gnu.org/software/gettext/manual/gettext.html](http://www.gnu.org/software/gettext/manual/gettext.html)
### Scenario 1. How do I add a translation or fix an existing translation
1. Get PO-file 'OrcaSlicer_xx.pot' from corresponding sub-folder here:
[https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n](https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n)
2. Open this file in PoEdit as "Edit a translation"
3. Apply your corrections to the translation
4. Push changed OrcaSlicer_xx.po into the original folder
5. copy OrcaSlicer_xx.mo into resources/i18n/xx and rename it to OrcaSlicer.mo, then push the changed file.
### Scenario 2. How do I add a new language support
1. Get file OrcaSlicer.pot here :
[https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n](https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n)
2. Open it in PoEdit for "Create new translation"
3. Select Translation Language (for example French).
4. As a result you will have fr.po - the file containing translation to French.
Notice. When the translation is complete you need to:
- Rename the file to OrcaSlicer_fr.po
- Click "Save file" button. OrcaSlicer_fr.mo will be created immediately
- Bambu_Studio_fr.po needs to be copied into the sub-folder fr of [https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n](https://github.com/softfever/OrcaSlicer/tree/master/localization/i18n), and be pushed
- copy OrcaSlicer_xx.mo into resources/i18n/xx and rename it to OrcaSlicer.mo, then push the changed file.
( name of folder "fr" means "French" - the translation language).
### Scenario 3. How do I add a new text resource when implementing a feature to OrcaSlicer
Each string resource in OrcaSlicer available for translation needs to be explicitly marked using L() macro like this:
```C++
auto msg = L("This message to be localized")
```
To get translated text use one of needed macro/function (`_(s)` or `_CHB(s)` ).
If you add new file resource, add it to the list of files containing macro `L()`
### Scenario 4. How do I use GNUgettext to localize my own application taking OrcaSlicer as an example
1. For convenience create a list of files with this macro `L(s)`. We have
https://github.com/softfever/OrcaSlicer/blob/master/localization/i18n/list.txt.
2. Create template file(*.POT) with GNUgettext command:
```shell
xgettext --keyword=L --add-comments=TRN --from-code=UTF-8 --debug -o OrcaSlicer.pot -f list.txt
```
Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding
Use flag `--debug` to correctly extract formatted strings(used %d, %s etc.)
3. Create PO- and MO-files for your project as described above.
4. To merge old PO-file with strings from created new POT-file use command:
```shell
msgmerge -N -o new.po old.po new.pot
```
Use option `-N` to not using fuzzy matching when an exact match is not found.
5. To concatenate old PO-file with strings from new PO-file use command:
```shell
msgcat -o new.po old.po
```
6. Create an English translation catalog with command:
```shell
msgen -o new.po old.po
```
> [!NOTE]
> In this Catalog it will be totally same strings for initial text and translated.
When you have Catalog to translation open POT or PO file in PoEdit and start translating.
## General guidelines for OrcaSlicer translators
- We recommend using _PoEdit_ application for translation (as described above). It will help you eliminate most punctuation errors and will show you strings with "random" translations (if the fuzzy parameter was used).
- To check how the translated text looks on the UI elements, test it :) If you use _PoEdit_, all you need to do is save the file. At this point, a MO file will be created. Rename it OrcaSlicer.mo, and you can run OrcaSlicer (see above).
- If you see an encoding error (garbage characters instead of Unicode) somewhere in OrcaSlicer, report it. It is likely not a problem of your translation, but a bug in the software.
- See on which UI elements the translated phrase will be used. Especially if it's a button, it is very important to decide on the translation and not write alternative translations in parentheses, as this will significantly increase the width of the button, which is sometimes highly undesirable:
- If you decide to use autocorrect or any batch processing tool, the output requires very careful proofreading. It is very easy to make it do changes that break things big time.
- **Any formatting parts of the phrases must remain unchanged.** For example, you should not change `%1%` to `%1 %`, you should not change `%%` to `%` (for percent sign) and similar. This will lead to application crashes.
- Please pay attention to spaces, line breaks (\n) and punctuation marks. **Don't add extra line breaks.** This is especially important for parameter names.
- Description of the parameters should not contain units of measurement. For example, "Enable fan if layer print time is less than ~~n seconds~~"
- For units of measurement, use the international system of units. Use "s" instead of "sec".
- If the phrase doesn't have a dot at the end, don't add it. And if it does, then don't forget to :)
- It is useful to stick to the same terminology in the application (especially with basic terms such as "filament" and similar). Stay consistent. Otherwise it will confuse users.

View File

@@ -1,47 +0,0 @@
# Preset and Bundle
This page deals with the explanation for 3 classes in the code.
## [`Preset`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/libslic3r/Preset.hpp)
As the name might suggest this class deals with presets for various things. It defines an enum `Type` which basically tells you what kind of data the present contains. Below are a few explained and there corresponding UI elements
> [!WARNING]
> There is a lot of outdated and legacy code in the code base.
- `TYPE_PRINT`: Refers to a process preset. It's called 'Print' probably due to some legacy code.
![process-preset-full](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/process-preset-full.png?raw=true)
- `TYPE_FILAMENT`: As the name suggests this preset is for filaments
![filament-preset](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/filament-preset.png?raw=true)
- `TYPE_PRINTER`: Preset for printers.
![printer-preset](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/printer-preset.png?raw=true)
There are other preset types but some of them are for SLA. Which is legacy code, since SLA printers are no longer supported. Above 3 are the important types.
## [`PresetBundle`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/libslic3r/PresetBundle.hpp)
This is a bundle containing a few types of `PresetCollection`. One bundle has presets for some printers, filaments and some processes (TYPE_PRINT).
`PresetCollection prints`\
`PresetCollection filaments`\
`PrinterPresetCollection printers`
each one of these contains a collection of processes, filaments and printers respectively.\
> [!IMPORTANT]
> Printers, filaments and processes in the bundle don't all have to be compatible with each other. In fact all the saved presets are stored in one `PresetBundle`. The `PresetBundle` is loaded on start up. The list of filaments and processes shown for a particular printer is a subset of `filaments` and `prints` `PresetCollection`s.
## [`PresetCollection`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/libslic3r/Preset.hpp)
`PrinterPresetCollection` is a class derived from `PresetCollection`.
These contain a collection of presets. The presets could be of any type.\
functions of note here are:
`get_edited_preset()`: returns the current selected preset along with any modifications the user has made.\
`get_selected_preset()`: returns the current selected preset without the modifications the user has made.

View File

@@ -1,28 +0,0 @@
# Application Structure Overview
WIP...
> [!WARNING]
> !! incomplete, possibly inaccurate, being updated with new info !!
## [`Plater`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/slic3r/GUI/Plater.hpp)
Refers to the entire application. The whole view, file loading, project saving and loading is all managed by this class. This class contains members for the model viewer, the sidebar, gcode viewer and everything else.
## [`Sidebar`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/slic3r/GUI/Plater.hpp)
This is relating the the sidebar in the application window
![full-sidebar](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/full-sidebar.png?raw=true)
## [`ComboBox`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/slic3r/GUI/Widgets/ComboBox.hpp)
The drop down menus where you can see and select presets
![combobox](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/combobox.png?raw=true)
## [`Tab`](https://github.com/SoftFever/OrcaSlicer/blob/main/src/slic3r/GUI/Tab.hpp)
Refers to the various windows with settings. e.g. the Popup to edit printer or filament preset. Also the section to edit process preset and the object list. These 4 are managed by `TabPrinter`, `TabFilament`, `TabPrint` and `TabPrintModel` respectively.
![tab-popup](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/images/GUI/tab-popup.png?raw=true)

View File

@@ -1,44 +0,0 @@
## Slicing Call Hierarchy
The Slicing logic is not the easiest to locate in the code base. Below is a flow diagram of function calls that are made after clicking the `Slice Plate` button in the UI. Most of the processing happens in different threads. Note the calls after `BackgroundSlicingProcess::start()`, but this is how you can find the slicing logic.
```mermaid
flowchart TD
A["Slice plate"] --> B["void Plater::priv::on_action_slice_plate(SimpleEvent&)"]
B --> C["void Plater::reslice()"]
C --> D["bool Plater::priv::restart_background_process(unsigned int state)"]
D --> E["bool BackgroundSlicingProcess::start()"]
E --> F["void BackgroundSlicingProcess::thread_proc_safe_seh_throw()"]
F --> G["unsigned long BackgroundSlicingProcess::thread_proc_safe_seh()"]
G --> H["void BackgroundSlicingProcess::thread_proc_safe()"]
H --> I["void BackgroundSlicingProcess::thread_proc()"]
I --> J["void BackgroundSlicingProcess::call_process_seh_throw(std::exception_ptr &ex)"]
J --> K["unsigned long BackgroundSlicingProcess::call_process_seh(std::exception_ptr &ex)"]
K --> L["void BackgroundSlicingProcess::call_process(std::exception_ptr &ex)"]
L --> M["void BackgroundSlicingProcess::process_fff()"]
M --> N["void Print::process(long long *time_cost_with_cache, bool use_cache)"]
N --> O["void PrintObject::make_perimeters()"]
O --> P["void PrintObject::slice()"]
%% Labels for libraries
subgraph G1 [libSlic3r_gui]
B
C
D
E
F
G
H
I
J
K
L
M
end
subgraph G2 [libSlic3r]
N
O
P
end
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Some files were not shown because too many files have changed in this diff Show More