Merge remote-tracking branch 'remote/main' into dev/h2d-2
# Conflicts: # src/libslic3r/PrintConfig.cpp # src/slic3r/GUI/ConfigManipulation.cpp
167
.github/workflows/validate-documentation.yml
vendored
@@ -58,6 +58,45 @@ jobs:
|
|||||||
return $null
|
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
|
# Initialize
|
||||||
$tabFile = Join-Path $PWD "src/slic3r/GUI/Tab.cpp"
|
$tabFile = Join-Path $PWD "src/slic3r/GUI/Tab.cpp"
|
||||||
$docDir = Join-Path $PWD 'doc'
|
$docDir = Join-Path $PWD 'doc'
|
||||||
@@ -99,8 +138,35 @@ jobs:
|
|||||||
if ($inCodeFence) { continue }
|
if ($inCodeFence) { continue }
|
||||||
|
|
||||||
$lineForParsing = [regex]::Replace($line, '`[^`]*`', '')
|
$lineForParsing = [regex]::Replace($line, '`[^`]*`', '')
|
||||||
foreach ($linkMatch in [regex]::Matches($lineForParsing, '(?<!!)[^\]]*\]\(([^)]+)\)')) {
|
|
||||||
$destRaw = $linkMatch.Groups[1].Value.Trim()
|
# 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
|
# Handle internal fragments
|
||||||
if ($destRaw.StartsWith('#')) {
|
if ($destRaw.StartsWith('#')) {
|
||||||
@@ -207,54 +273,39 @@ jobs:
|
|||||||
}
|
}
|
||||||
if ($inCodeFence) { continue }
|
if ($inCodeFence) { continue }
|
||||||
|
|
||||||
$lineForParsing = [regex]::Replace($line, '`[^`]*`', '')
|
# Use the unified image detection function
|
||||||
|
$imagesInLine = Get-ImagesFromLine $line
|
||||||
|
|
||||||
# Process markdown and HTML images
|
foreach ($image in $imagesInLine) {
|
||||||
$imagePatterns = @(
|
$altText = $image.AltText
|
||||||
@{ Pattern = "!\[([^\]]*)\]\(([^)]+)\)"; Type = "Markdown"; AltGroup = 1; UrlGroup = 2 }
|
$url = $image.Url
|
||||||
@{ Pattern = '<img\s+[^>]*>'; Type = "HTML"; AltGroup = -1; UrlGroup = -1 }
|
$imageMatch = $image.Match
|
||||||
)
|
$imageType = $image.Type
|
||||||
|
|
||||||
foreach ($pattern in $imagePatterns) {
|
if (-not $altText.Trim() -and $url) {
|
||||||
foreach ($match in [regex]::Matches($lineForParsing, $pattern.Pattern)) {
|
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch "[$imageType] Missing alt text for image" "Image"
|
||||||
$altText = ""
|
} elseif ($url -and $altText) {
|
||||||
$url = ""
|
# Validate URL format and file existence
|
||||||
|
if ($url -match $expectedUrlPattern) {
|
||||||
if ($pattern.Type -eq "Markdown") {
|
$relativePathInUrl = $matches[1]
|
||||||
$altText = $match.Groups[$pattern.AltGroup].Value
|
$fileNameFromUrl = [System.IO.Path]::GetFileNameWithoutExtension($relativePathInUrl)
|
||||||
$url = $match.Groups[$pattern.UrlGroup].Value
|
|
||||||
} else {
|
if ($altText -ne $fileNameFromUrl) {
|
||||||
# Extract from HTML
|
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $imageMatch "[$imageType] Alt text `"$altText`" ≠ filename `"$fileNameFromUrl`"" "Image"
|
||||||
$imgTag = $match.Value
|
|
||||||
if ($imgTag -match 'alt\s*=\s*[`"'']([^`"'']*)[`"'']') { $altText = $matches[1] }
|
|
||||||
if ($imgTag -match 'src\s*=\s*[`"'']([^`"'']*)[`"'']') { $url = $matches[1] }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $altText.Trim() -and $url) {
|
|
||||||
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $match.Value "[$($pattern.Type)] 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) $match.Value "[$($pattern.Type)] Alt text `"$altText`" ≠ filename `"$fileNameFromUrl`"" "Image"
|
|
||||||
}
|
|
||||||
|
|
||||||
$expectedImagePath = Join-Path $PWD ($relativePathInUrl -replace "/", "\")
|
|
||||||
if (-not (Test-Path $expectedImagePath)) {
|
|
||||||
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $match.Value "[$($pattern.Type)] 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 = "[$($pattern.Type)] URL format issues: " + ($urlIssues -join '; ')
|
|
||||||
$brokenReferences += Add-BrokenReference $relPath ($lineNumber + 1) $match.Value $issueText "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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,22 +365,4 @@ jobs:
|
|||||||
} else {
|
} else {
|
||||||
Write-Host "::notice::All documentation is valid!"
|
Write-Host "::notice::All documentation is valid!"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Comment on PR
|
|
||||||
if: failure() && github.event_name == 'pull_request'
|
|
||||||
uses: actions/github-script@v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const validationErrors = process.env.VALIDATION_ERRORS || '';
|
|
||||||
|
|
||||||
const body = `❌ **Documentation validation failed**
|
|
||||||
|
|
||||||
${validationErrors || 'Please check the workflow logs for details about the validation errors.'}`;
|
|
||||||
|
|
||||||
github.rest.issues.createComment({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: body
|
|
||||||
})
|
|
||||||
11
README.md
@@ -29,10 +29,13 @@ Optimize your prints with ultra-fast slicing, intelligent support generation, an
|
|||||||
<table border="2" style="border-color: #ffa500; background-color:rgb(232, 220, 180); color: #856404;">
|
<table border="2" style="border-color: #ffa500; background-color:rgb(232, 220, 180); color: #856404;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<strong>⚠️ CAUTION:</strong><br><br>
|
<strong>⚠️ CAUTION:</strong><br>
|
||||||
Several clickbait and malicious websites, such as orca-slicer.com and orcaslicer.net, are pretending to be the official OrcaSlicer site. These sites may redirect you to dangerous downloads or contain misleading information. Our only official website is www.orcaslicer.com.
|
Several clickbait and malicious websites, such as <b>orca-slicer[.]com</b> and <b>orcaslicer[.]net</b>, are pretending to be the official OrcaSlicer site. These sites may redirect you to dangerous downloads or contain misleading information.<br>
|
||||||
<br><br>
|
<b>Our only official website is <a href="https://www.orcaslicer.com/">www.orcaslicer.com</a>.</b><br><br>
|
||||||
If you come across any of these in search results, please <a href="https://safebrowsing.google.com/safebrowsing/report_phish/?">report them as unsafe or phishing</a> to help keep the community secure.
|
If you come across any of these in search results, please <b>report them</b> as unsafe or phishing to help keep the community secure with:<br>
|
||||||
|
- <a href="https://safebrowsing.google.com/safebrowsing/report_phish/">Google Safe Browsing</a><br>
|
||||||
|
- <a href="https://www.microsoft.com/en-us/wdsi/support/report-unsafe-site">Microsoft Security Intelligence</a><br>
|
||||||
|
- <a href="https://ipthreat.net/tools/reportphishing">IPThreat</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -84,19 +84,34 @@ How to building with Visual Studio 2022 on Windows 64-bit.
|
|||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!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.
|
> If you encounter issues, you can try to uninstall ZLIB from your Vcpkg library.
|
||||||
|
|
||||||
3. If successful, you will find the VS 2022 solution file in:
|
3. If successful, you will find the Visual Studio solution file in:
|
||||||
```shell
|
```shell
|
||||||
build\OrcaSlicer.sln
|
build\OrcaSlicer.sln
|
||||||
```
|
```
|
||||||
|
4. Open the solution in Visual Studio, set the build configuration to `Release` and run the `Local Windows Debugger`.
|
||||||
|

|
||||||
|
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]
|
> [!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.
|
> 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).
|
> 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.
|
> 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.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!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.
|
> 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
|
## MacOS 64-bit
|
||||||
|
|||||||
@@ -139,9 +139,9 @@ The following sample JSON file shows how to create a new generic filament profil
|
|||||||
> 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.
|
> 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:
|
> To force OrcaSlicer to load your updated profiles:
|
||||||
> 1. **Access the configuration folder**: Go to **Help** → **Show Configuration Folder**
|
> 1. **Access the configuration folder**: Go to **Help** → **Show Configuration Folder**
|
||||||
> 
|
> 
|
||||||
> 2. **Clear the cache**: Delete the `system` folder to remove cached profiles
|
> 2. **Clear the cache**: Delete the `system` folder to remove cached profiles
|
||||||
> 
|
> 
|
||||||
> 3. **Restart OrcaSlicer**: Launch the application to load your updated profiles
|
> 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.
|
> This process forces OrcaSlicer to update its profile cache from the source files in the `resources/profiles/` directory.
|
||||||
|
|
||||||
|
|||||||
BIN
doc/images/develop/compile_vs2022_local_debugger.png
Normal file
|
After Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 28 KiB |
BIN
doc/images/develop/go-to-configuration-folder.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
doc/images/develop/profile-delete-system-folder.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 344 KiB |
BIN
doc/images/fill/fill-layer-time-variability.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
@@ -63,7 +63,7 @@ To mitigate this effect, OrcaSlicer allows you to specify a negative distance th
|
|||||||
|
|
||||||
## Precise wall
|
## Precise wall
|
||||||
|
|
||||||
The 'Precise Wall' is a distinctive feature introduced by OrcaSlicer, aimed at improving the dimensional accuracy of prints and minimizing layer inconsistencies by slightly increasing the spacing between the outer wall and the inner wall.
|
The 'Precise Wall' is a distinctive feature introduced by OrcaSlicer, aimed at improving the dimensional accuracy of prints and minimizing layer inconsistencies by slightly increasing the spacing between the outer wall and the inner wall when printing in [Inner Outer wall order](quality_settings_wall_and_surfaces#innerouter).
|
||||||
|
|
||||||
### Technical explanation
|
### Technical explanation
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ Highly recommended for detailed or aesthetic prints.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This feature is not compatible with Timelapse mode, as it can cause unexpected travel moves.
|
||||||
|
|
||||||
### Max detour length
|
### Max detour length
|
||||||
|
|
||||||
Defines the maximum distance the printer is allowed to detour to avoid crossing a wall.
|
Defines the maximum distance the printer is allowed to detour to avoid crossing a wall.
|
||||||
|
|||||||
BIN
doc/print_settings/strength/infill-analysis/infill-analysis.7z
Normal file
@@ -1,58 +1,95 @@
|
|||||||
# Patterns
|
# Patterns
|
||||||
|
|
||||||
Patterns determine how material is distributed within a print. Different patterns can affect strength, flexibility and print speed using the same density setting.
|
Patterns determine how material is distributed within a print. Different patterns can affect strength, flexibility and print speed using the same density setting.
|
||||||
|
The infill pattern also impacts the uniformity of the layer times, since the patterns may be constant, or present significant variations between adjacent layers.
|
||||||
|
|
||||||
There is no one-size-fits-all solution, as the best pattern depends on the specific print and its requirements.
|
There is no one-size-fits-all solution, as the best pattern depends on the specific print and its requirements.
|
||||||
|
|
||||||
Many patterns may look similar and have similar overall specifications, but they can behave very differently in practice.
|
Many patterns may look similar and have similar overall specifications, but they can behave very differently in practice.
|
||||||
As most settings in 3D printing, experience is the best way to determine which pattern works best for your specific needs.
|
As most settings in 3D printing, experience is the best way to determine which pattern works best for your specific needs.
|
||||||
|
|
||||||
## Patterns Quick Reference
|
## Analysis parameters
|
||||||
|
|
||||||
| | Pattern | Applies to | X-Y Strength | Z Strength | Material Usage | Print Time |
|
### Strength
|
||||||
|---|---|---|---|---|---|---|
|
|
||||||
|  | [Monotonic](#monotonic) | - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Normal | Normal | Normal-High | Normal-Low |
|
- **X-Y Direction**: The strength of the print in the "Horizontal" X-Y plane. Affected by the pattern's connections between walls, contact between layers, and path.
|
||||||
|  | [Monotonic line](#monotonic-line) | - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Normal | Normal | Normal | Normal |
|
- **Z Direction**: The strength of the print in the "Vertical" Z direction. Affected by contact between layers.
|
||||||
|  | [Rectilinear](#rectilinear) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** - **[Ironing](quality_settings_ironing)** | Normal-Low | Low | Normal | Normal-Low |
|
|
||||||
|  | [Aligned Rectilinear](#aligned-rectilinear) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Normal-Low | Normal | Normal | Normal-Low |
|
### Material Usage
|
||||||
|  | [Zig Zag](#zig-zag) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-Low | Low | Normal | Normal-Low |
|
|
||||||
|  | [Cross Zag](#cross-zag) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal | Low | Normal | Normal-Low |
|
Not all patterns use the same amount of material due to their **Density Calculations** and adjustments to the paths.
|
||||||
|  | [Locked Zag](#locked-zag) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-Low | Normal-Low | Low | Extra-High |
|
This leads to patterns that do not use the specified percentage but rather variations of it.
|
||||||
|  | [Line](#line) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Low | Low | Normal-High | Normal-Low |
|
|
||||||
|  | [Grid](#grid) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Normal-High | Normal-Low |
|
### Print Time
|
||||||
|  | [Triangles](#triangles) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | Normal | Normal-High | Normal-Low |
|
|
||||||
|  | [Tri-hexagon](#tri-hexagon) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | Normal-High | Normal-High | Normal-Low |
|
Print time can vary significantly between patterns due to differences in their pathing and infill strategies.
|
||||||
|  | [Cubic](#cubic) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Normal-High | Normal-Low |
|
Some patterns may complete faster due to more efficient use of the print head's movement, while others may take longer due to more complex paths.
|
||||||
|  | [Adaptive Cubic](#adaptive-cubic) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-High | Normal-High | Normal | Low |
|
|
||||||
|  | [Quarter Cubic](#quarter-cubic) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Normal-High | Normal-Low |
|
|
||||||
|  | [Support Cubic](#support-cubic) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Low | Low | Normal | Extra-Low |
|
|
||||||
|  | [Lightning](#lightning) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Low | Low | Low | Ultra-Low |
|
|
||||||
|  | [Honeycomb](#honeycomb) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Low | Ultra-High |
|
|
||||||
|  | [3D Honeycomb](#3d-honeycomb) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-High | Normal-High | Low | High |
|
|
||||||
|  | [Lateral Honeycomb](#lateral-honeycomb) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-Low | Normal-Low | Normal-High | Normal-Low |
|
|
||||||
|  | [Lateral Lattice](#lateral-lattice) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-Low | Low | Normal-High | Normal-Low |
|
|
||||||
|  | [Cross Hatch](#cross-hatch) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-High | Normal-High | Normal-Low | Normal-High |
|
|
||||||
|  | [TPMS-D](#tpms-d) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Normal-Low | High |
|
|
||||||
|  | [TPMS-FK](#tpms-fk) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | Normal-High | Normal-High | Low | High |
|
|
||||||
|  | [Gyroid](#gyroid) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** | High | High | Normal-Low | Normal-High |
|
|
||||||
|  | [Concentric](#concentric) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** - **[Ironing](quality_settings_ironing)** | Low | Normal | Normal-High | Normal-Low |
|
|
||||||
|  | [Hilbert Curve](#hilbert-curve) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Low | Normal | Low | High |
|
|
||||||
|  | [Archimedean Chords](#archimedean-chords) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Low | Normal | Normal-High | Normal-Low |
|
|
||||||
|  | [Octagram Spiral](#octagram-spiral) | - **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)** - **[Surface](strength_settings_top_bottom_shells)** | Low | Normal | Normal-Low | Normal |
|
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> You can download [infill_desc_calculator.xlsx](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/print_settings/strength/infill_desc_calculator.xlsx?raw=true) used to calculate the values above.
|
> OrcaSlicer Time estimations are not always accurate, especially with complex patterns.
|
||||||
|
> This analysis was estimated with [Klipper Estimator](https://github.com/Annex-Engineering/klipper_estimator).
|
||||||
|
|
||||||
|
### Layer Time Variability
|
||||||
|
|
||||||
|
Layer time variability refers to the differences in time it takes to print each layer of a pattern. Some patterns may have consistent layer times, while others may experience significant fluctuations. These variations can potentially impact the outer appearance of the print due to differences in cooling and material flow between layers.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Patterns Quick Reference
|
||||||
|
|
||||||
|
| - | Pattern | Strength | Material Usage | Print Time | Layer time Variability |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| <img alt="param_monotonic" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_monotonic.svg?raw=true" height="45"> | [Monotonic](#monotonic) | X-Y: ⚪️ Normal<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_monotonicline" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_monotonicline.svg?raw=true" height="45"> | [Monotonic line](#monotonic-line) | X-Y: ⚪️ Normal<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_rectilinear" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_rectilinear.svg?raw=true" height="45"> | [Rectilinear](#rectilinear) | X-Y: ⚪️ Normal-Low<br> Z: 🟡 Low | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_alignedrectilinear" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_alignedrectilinear.svg?raw=true" height="45"> | [Aligned Rectilinear](#aligned-rectilinear) | X-Y: ⚪️ Normal-Low<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_zigzag" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_zigzag.svg?raw=true" height="45"> | [Zig Zag](#zig-zag) | X-Y: ⚪️ Normal-Low<br> Z: 🟡 Low | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_crosszag" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_crosszag.svg?raw=true" height="45"> | [Cross Zag](#cross-zag) | X-Y: ⚪️ Normal<br> Z: 🟡 Low | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_lockedzag" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_lockedzag.svg?raw=true" height="45"> | [Locked Zag](#locked-zag) | X-Y: ⚪️ Normal-Low<br> Z: ⚪️ Normal-Low | ⚪️ Normal-High | ⚪️ Normal-High | 🟢 None |
|
||||||
|
| <img alt="param_line" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_line.svg?raw=true" height="45"> | [Line](#line) | X-Y: 🟡 Low<br> Z: 🟡 Low | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_grid" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_grid.svg?raw=true" height="45"> | [Grid](#grid) | X-Y: 🟣 High<br> Z: 🟣 High | ⚪️ Normal | 🟣 Low | 🟢 None |
|
||||||
|
| <img alt="param_triangles" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_triangles.svg?raw=true" height="45"> | [Triangles](#triangles) | X-Y: 🟣 High<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_tri-hexagon" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_tri-hexagon.svg?raw=true" height="45"> | [Tri-hexagon](#tri-hexagon) | X-Y: 🟣 High<br> Z: 🔘 Normal-High | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_cubic" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_cubic.svg?raw=true" height="45"> | [Cubic](#cubic) | X-Y: 🟣 High<br> Z: 🟣 High | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_adaptivecubic" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_adaptivecubic.svg?raw=true" height="45"> | [Adaptive Cubic](#adaptive-cubic) | X-Y: 🔘 Normal-High<br> Z: 🔘 Normal-High | 🟣 Low | 🟣 Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_quartercubic" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_quartercubic.svg?raw=true" height="45"> | [Quarter Cubic](#quarter-cubic) | X-Y: 🟣 High<br> Z: 🟣 High | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_supportcubic" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_supportcubic.svg?raw=true" height="45"> | [Support Cubic](#support-cubic) | X-Y: 🟡 Low<br> Z: 🟡 Low | 🔵 Extra-Low | 🔵 Extra-Low | 🔴 Likely Noticeable |
|
||||||
|
| <img alt="param_lightning" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_lightning.svg?raw=true" height="45"> | [Lightning](#lightning) | X-Y: 🟡 Low<br> Z: 🟡 Low | 🟢 Ultra-Low | 🟢 Ultra-Low | 🔴 Likely Noticeable |
|
||||||
|
| <img alt="param_honeycomb" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_honeycomb.svg?raw=true" height="45"> | [Honeycomb](#honeycomb) | X-Y: 🟣 High<br> Z: 🟣 High | 🟡 High | 🔴 Ultra-High | 🟢 None |
|
||||||
|
| <img alt="param_3dhoneycomb" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_3dhoneycomb.svg?raw=true" height="45"> | [3D Honeycomb](#3d-honeycomb) | X-Y: 🔘 Normal-High<br> Z: 🔘 Normal-High | 🔘 Normal-Low | 🟠 Extra-High | 🟡 Possibly Noticeable |
|
||||||
|
| <img alt="param_lateral-honeycomb" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_lateral-honeycomb.svg?raw=true" height="45"> | [Lateral Honeycomb](#lateral-honeycomb) | X-Y: ⚪️ Normal-Low<br> Z: ⚪️ Normal-Low | ⚪️ Normal | 🔘 Normal-Low | 🟡 Possibly Noticeable |
|
||||||
|
| <img alt="param_lateral-lattice" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_lateral-lattice.svg?raw=true" height="45"> | [Lateral Lattice](#lateral-lattice) | X-Y: ⚪️ Normal-Low<br> Z: 🟡 Low | ⚪️ Normal | 🔘 Normal-Low | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_crosshatch" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_crosshatch.svg?raw=true" height="45"> | [Cross Hatch](#cross-hatch) | X-Y: 🔘 Normal-High<br> Z: 🔘 Normal-High | ⚪️ Normal | 🟡 High | 🔴 Likely Noticeable |
|
||||||
|
| <img alt="param_tpmsd" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_tpmsd.svg?raw=true" height="45"> | [TPMS-D](#tpms-d) | X-Y: 🟣 High<br> Z: 🟣 High | ⚪️ Normal | 🟡 High | 🟡 Possibly Noticeable |
|
||||||
|
| <img alt="param_tpmsfk" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_tpmsfk.svg?raw=true" height="45"> | [TPMS-FK](#tpms-fk) | X-Y: 🔘 Normal-High<br> Z: 🔘 Normal-High | ⚪️ Normal | 🔴 Ultra-High | 🟡 Possibly Noticeable |
|
||||||
|
| <img alt="param_gyroid" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_gyroid.svg?raw=true" height="45"> | [Gyroid](#gyroid) | X-Y: 🟣 High<br> Z: 🟣 High | ⚪️ Normal | 🔴 Ultra-High | 🔵 Unnoticeable |
|
||||||
|
| <img alt="param_concentric" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_concentric.svg?raw=true" height="45"> | [Concentric](#concentric) | X-Y: 🟡 Low<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_hilbertcurve" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_hilbertcurve.svg?raw=true" height="45"> | [Hilbert Curve](#hilbert-curve) | X-Y: 🟡 Low<br> Z: ⚪️ Normal | ⚪️ Normal | 🟠 Extra-High | 🟢 None |
|
||||||
|
| <img alt="param_archimedeanchords" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_archimedeanchords.svg?raw=true" height="45"> | [Archimedean Chords](#archimedean-chords) | X-Y: 🟡 Low<br> Z: ⚪️ Normal | ⚪️ Normal | 🔘 Normal-Low | 🟢 None |
|
||||||
|
| <img alt="param_octagramspiral" src="https://github.com/SoftFever/OrcaSlicer/blob/main/resources/images/param_octagramspiral.svg?raw=true" height="45"> | [Octagram Spiral](#octagram-spiral) | X-Y: 🟡 Low<br> Z: ⚪️ Normal | ⚪️ Normal | ⚪️ Normal | 🟢 None |
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This estimations are based in a Cube model to maintain consistency.
|
||||||
|
> This **WILL NOT** be the same for all models and only serves as a standard guideline.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> You can see how this analysis was made in [infill-analysis](https://github.com/SoftFever/OrcaSlicer/tree/main/doc/print_settings/strength/infill-analysis) folder:
|
||||||
|
> - [Infill calculator Project](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/print_settings/strength/infill-analysis/infill_calculator.3mf?raw=true) to generate the gcode files and images.
|
||||||
|
> - [infill_desc_calculator.xlsx](https://github.com/SoftFever/OrcaSlicer/blob/main/doc/print_settings/strength/infill-analysis/infill_desc_calculator.xlsx?raw=true) used to calculate the values above.
|
||||||
|
> - Time, and material usage where simulated with the same [Klipper Estimator](https://github.com/Annex-Engineering/klipper_estimator) values to maintain consistency.
|
||||||
|
|
||||||
## Monotonic
|
## Monotonic
|
||||||
|
|
||||||
[Rectilinear](#rectilinear) in a uniform direction for a smoother visual surface.
|
[Rectilinear](#rectilinear) in a uniform direction for a smoother visual surface.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** ⚪️ Normal
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
@@ -63,12 +100,14 @@ As most settings in 3D printing, experience is the best way to determine which p
|
|||||||
|
|
||||||
[Monotonic](#monotonic) but avoids overlapping with the perimeter, reducing excess material at joints. May introduce visible seams and increase print time.
|
[Monotonic](#monotonic) but avoids overlapping with the perimeter, reducing excess material at joints. May introduce visible seams and increase print time.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** ⚪️ Normal
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
@@ -79,14 +118,17 @@ As most settings in 3D printing, experience is the best way to determine which p
|
|||||||
|
|
||||||
Parallel lines spaced according to infill density. Each layer is printed perpendicular to the previous, resulting in low vertical bonding. Consider using new [Zig Zag](#zig-zag) infill instead.
|
Parallel lines spaced according to infill density. Each layer is printed perpendicular to the previous, resulting in low vertical bonding. Consider using new [Zig Zag](#zig-zag) infill instead.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
- **[Ironing](quality_settings_ironing)**
|
- **[Ironing](quality_settings_ironing)**
|
||||||
|
|
||||||
@@ -97,14 +139,17 @@ Parallel lines spaced according to infill density. Each layer is printed perpend
|
|||||||
Parallel lines spaced by the infill spacing, each layer printed in the same direction as the previous layer. Good horizontal strength perpendicular to the lines, but terrible in parallel direction.
|
Parallel lines spaced by the infill spacing, each layer printed in the same direction as the previous layer. Good horizontal strength perpendicular to the lines, but terrible in parallel direction.
|
||||||
Recommended with layer anchoring to improve not perpendicular strength.
|
Recommended with layer anchoring to improve not perpendicular strength.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
|
|
||||||

|

|
||||||
@@ -113,12 +158,14 @@ Recommended with layer anchoring to improve not perpendicular strength.
|
|||||||
|
|
||||||
Similar to [rectilinear](#rectilinear) with consistent pattern between layers. Allows you to add a Symmetric infill Y axis for models with two symmetric parts.
|
Similar to [rectilinear](#rectilinear) with consistent pattern between layers. Allows you to add a Symmetric infill Y axis for models with two symmetric parts.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -128,12 +175,14 @@ Similar to [rectilinear](#rectilinear) with consistent pattern between layers. A
|
|||||||
|
|
||||||
Similar to [Zig Zag](#zig-zag) but displacing each layer with Infill shift step parameter.
|
Similar to [Zig Zag](#zig-zag) but displacing each layer with Infill shift step parameter.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** ⚪️ Normal
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -141,14 +190,18 @@ Similar to [Zig Zag](#zig-zag) but displacing each layer with Infill shift step
|
|||||||
|
|
||||||
## Locked Zag
|
## Locked Zag
|
||||||
|
|
||||||
Adaptive version of [Zig Zag](#zig-zag) adding an external skin texture to interlock layers and a low material skeleton.
|
Version of [Zig Zag](#zig-zag) that adds extra skin.
|
||||||
|
When using this fill, you can individually modify the density of the skeleton and skin, as well as the size of the skin and how much interconnection there is between the skin and the skeleton (a lock depth of 50% of the skin depth is recommended).
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-Low
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
- **Density Calculation:** Same as [Zig Zag](#zig-zag) but increasing near walls
|
- **Vertical (Z):** ⚪️ Normal-Low
|
||||||
- **Material Usage:** Normal-High
|
- **Density Calculation:** Similar to [Zig Zag](#zig-zag).
|
||||||
- **Print Time:** Extra-High
|
Skin density * ( Infill Area - Skin Area + lock depth area) + ( Skin density * Skin area).
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material Usage:** ⚪️ Normal-High
|
||||||
|
- **Print Time:** ⚪️ Normal-High
|
||||||
|
- **Material/Time (Higher better):** ⚪️ Normal
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -158,12 +211,14 @@ Adaptive version of [Zig Zag](#zig-zag) adding an external skin texture to inter
|
|||||||
|
|
||||||
Similar to [rectilinear](#rectilinear), but each line is slightly rotated to improve print speed.
|
Similar to [rectilinear](#rectilinear), but each line is slightly rotated to improve print speed.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -173,12 +228,14 @@ Similar to [rectilinear](#rectilinear), but each line is slightly rotated to imp
|
|||||||
|
|
||||||
Two-layer pattern of perpendicular lines, forming a grid. Overlapping points may cause noise or artifacts.
|
Two-layer pattern of perpendicular lines, forming a grid. Overlapping points may cause noise or artifacts.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🟣 Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -188,12 +245,14 @@ Two-layer pattern of perpendicular lines, forming a grid. Overlapping points may
|
|||||||
|
|
||||||
Triangle-based grid, offering strong X-Y strength but with triple overlaps at intersections.
|
Triangle-based grid, offering strong X-Y strength but with triple overlaps at intersections.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -203,12 +262,14 @@ Triangle-based grid, offering strong X-Y strength but with triple overlaps at in
|
|||||||
|
|
||||||
Similar to the [triangles](#triangles) pattern but offset to prevent triple overlaps at intersections. This design combines triangles and hexagons, providing excellent X-Y strength.
|
Similar to the [triangles](#triangles) pattern but offset to prevent triple overlaps at intersections. This design combines triangles and hexagons, providing excellent X-Y strength.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🔘 Normal-High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -218,12 +279,14 @@ Similar to the [triangles](#triangles) pattern but offset to prevent triple over
|
|||||||
|
|
||||||
3D cube pattern with corners facing down, distributing force in all directions. Triangles in the horizontal plane provide good X-Y strength.
|
3D cube pattern with corners facing down, distributing force in all directions. Triangles in the horizontal plane provide good X-Y strength.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -233,12 +296,14 @@ Similar to the [triangles](#triangles) pattern but offset to prevent triple over
|
|||||||
|
|
||||||
[Cubic](#cubic) pattern with adaptive density: denser near walls, sparser in the center. Saves material and time while maintaining strength, ideal for large prints.
|
[Cubic](#cubic) pattern with adaptive density: denser near walls, sparser in the center. Saves material and time while maintaining strength, ideal for large prints.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-High
|
- **Horizontal (X-Y):** 🔘 Normal-High
|
||||||
|
- **Vertical (Z):** 🔘 Normal-High
|
||||||
- **Density Calculation:** Same as [Cubic](#cubic) but reduced in the center
|
- **Density Calculation:** Same as [Cubic](#cubic) but reduced in the center
|
||||||
- **Material Usage:** Low
|
- **Material Usage:** 🟣 Low
|
||||||
- **Print Time:** Low
|
- **Print Time:** 🟣 Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** ⚪️ Normal
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -248,12 +313,14 @@ Similar to the [triangles](#triangles) pattern but offset to prevent triple over
|
|||||||
|
|
||||||
[Cubic](#cubic) pattern with extra internal divisions, improving X-Y strength.
|
[Cubic](#cubic) pattern with extra internal divisions, improving X-Y strength.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -263,12 +330,14 @@ Similar to the [triangles](#triangles) pattern but offset to prevent triple over
|
|||||||
|
|
||||||
Support |Cubic is a variation of the [Cubic](#cubic) infill pattern that is specifically designed for support top layers. Will use more material than Lightning infill but will provide better strength. Nevertheless, it is still a low-density infill pattern.
|
Support |Cubic is a variation of the [Cubic](#cubic) infill pattern that is specifically designed for support top layers. Will use more material than Lightning infill but will provide better strength. Nevertheless, it is still a low-density infill pattern.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of layer before top shell layers
|
- **Density Calculation:** % of layer before top shell layers
|
||||||
- **Material Usage:** Extra-Low
|
- **Material Usage:** 🔵 Extra-Low
|
||||||
- **Print Time:** Extra-Low
|
- **Print Time:** 🔵 Extra-Low
|
||||||
- **Material/Time (Higher better):** Normal
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🔴 Likely Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -278,12 +347,14 @@ Support |Cubic is a variation of the [Cubic](#cubic) infill pattern that is spec
|
|||||||
|
|
||||||
Ultra-fast, ultra-low material infill. Designed for speed and efficiency, ideal for quick prints or non-structural prototypes.
|
Ultra-fast, ultra-low material infill. Designed for speed and efficiency, ideal for quick prints or non-structural prototypes.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of layer before top shell layers
|
- **Density Calculation:** % of layer before top shell layers
|
||||||
- **Material Usage:** Ultra-Low
|
- **Material Usage:** 🟢 Ultra-Low
|
||||||
- **Print Time:** Ultra-Low
|
- **Print Time:** 🟢 Ultra-Low
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material/Time (Higher better):** ⚪️ Normal-Low
|
||||||
|
- **Layer time Variability:** 🔴 Likely Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -293,12 +364,14 @@ Ultra-fast, ultra-low material infill. Designed for speed and efficiency, ideal
|
|||||||
|
|
||||||
Hexagonal pattern balancing strength and material use. Double walls in each hexagon increase material consumption.
|
Hexagonal pattern balancing strength and material use. Double walls in each hexagon increase material consumption.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** High
|
- **Material Usage:** 🟡 High
|
||||||
- **Print Time:** Ultra-High
|
- **Print Time:** 🔴 Ultra-High
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -308,12 +381,14 @@ Hexagonal pattern balancing strength and material use. Double walls in each hexa
|
|||||||
|
|
||||||
This infill tries to generate a printable honeycomb structure by printing squares and octagons maintaining a vertical angle high enough to maintain contact with the previous layer.
|
This infill tries to generate a printable honeycomb structure by printing squares and octagons maintaining a vertical angle high enough to maintain contact with the previous layer.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-High
|
- **Horizontal (X-Y):** 🔘 Normal-High
|
||||||
|
- **Vertical (Z):** 🔘 Normal-High
|
||||||
- **Density Calculation:** Unknown
|
- **Density Calculation:** Unknown
|
||||||
- **Material Usage:** Normal-Low
|
- **Material Usage:** 🔘 Normal-Low
|
||||||
- **Print Time:** High
|
- **Print Time:** 🟠 Extra-High
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🟡 Possibly Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -323,12 +398,14 @@ This infill tries to generate a printable honeycomb structure by printing square
|
|||||||
|
|
||||||
Vertical Honeycomb pattern. Acceptable torsional stiffness. Developed for low densities structures like wings. Improve over [Lateral Lattice](#lateral-lattice) offers same performance with lower densities.This infill includes a Overhang angle parameter to improve the point of contact between layers and reduce the risk of delamination.
|
Vertical Honeycomb pattern. Acceptable torsional stiffness. Developed for low densities structures like wings. Improve over [Lateral Lattice](#lateral-lattice) offers same performance with lower densities.This infill includes a Overhang angle parameter to improve the point of contact between layers and reduce the risk of delamination.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-Low
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal-Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟡 Possibly Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -338,12 +415,14 @@ Vertical Honeycomb pattern. Acceptable torsional stiffness. Developed for low de
|
|||||||
|
|
||||||
Low-strength pattern with good flexibility. You can adjust **Angle 1** and **Angle 2** to optimize the infill for your specific model. Each angle adjusts the plane of each layer generated by the pattern. 0° is vertical.
|
Low-strength pattern with good flexibility. You can adjust **Angle 1** and **Angle 2** to optimize the infill for your specific model. Each angle adjusts the plane of each layer generated by the pattern. 0° is vertical.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Low
|
- **Horizontal (X-Y):** ⚪️ Normal-Low
|
||||||
|
- **Vertical (Z):** 🟡 Low
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -354,12 +433,14 @@ Low-strength pattern with good flexibility. You can adjust **Angle 1** and **Ang
|
|||||||
Similar to [Gyroid](#gyroid) but with linear patterns, creating weak points at internal corners.
|
Similar to [Gyroid](#gyroid) but with linear patterns, creating weak points at internal corners.
|
||||||
Easier to slice but consider using [TPMS-D](#tpms-d) or [Gyroid](#gyroid) for better strength and flexibility.
|
Easier to slice but consider using [TPMS-D](#tpms-d) or [Gyroid](#gyroid) for better strength and flexibility.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-High
|
- **Horizontal (X-Y):** 🔘 Normal-High
|
||||||
|
- **Vertical (Z):** 🔘 Normal-High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-High
|
- **Print Time:** 🟡 High
|
||||||
- **Material/Time (Higher better):** Normal-Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🔴 Likely Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -369,12 +450,14 @@ Easier to slice but consider using [TPMS-D](#tpms-d) or [Gyroid](#gyroid) for be
|
|||||||
|
|
||||||
Triply Periodic Minimal Surface (Schwarz Diamond). Hybrid between [Cross Hatch](#cross-hatch) and [Gyroid](#gyroid), combining rigidity and smooth transitions. Isotropic and strong in all directions. This geometry is faster to slice than Gyroid, but slower than Cross Hatch.
|
Triply Periodic Minimal Surface (Schwarz Diamond). Hybrid between [Cross Hatch](#cross-hatch) and [Gyroid](#gyroid), combining rigidity and smooth transitions. Isotropic and strong in all directions. This geometry is faster to slice than Gyroid, but slower than Cross Hatch.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** High
|
- **Print Time:** 🟡 High
|
||||||
- **Material/Time (Higher better):** Normal-Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🟡 Possibly Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -384,12 +467,14 @@ Triply Periodic Minimal Surface (Schwarz Diamond). Hybrid between [Cross Hatch](
|
|||||||
|
|
||||||
Triply Periodic Minimal Surface (Fischer–Koch S) pattern. Its smooth, continuous geometry resembles trabecular bone microstructure, offering a balance between rigidity and energy absorption. Compared to [TPMS-D](#tpms-d), it has more complex curvature, which can improve load distribution and shock absorption in functional parts.
|
Triply Periodic Minimal Surface (Fischer–Koch S) pattern. Its smooth, continuous geometry resembles trabecular bone microstructure, offering a balance between rigidity and energy absorption. Compared to [TPMS-D](#tpms-d), it has more complex curvature, which can improve load distribution and shock absorption in functional parts.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Normal-High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal-High
|
- **Horizontal (X-Y):** 🔘 Normal-High
|
||||||
|
- **Vertical (Z):** 🔘 Normal-High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** High
|
- **Print Time:** 🔴 Ultra-High
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🟡 Possibly Noticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -399,12 +484,14 @@ Triply Periodic Minimal Surface (Fischer–Koch S) pattern. Its smooth, continuo
|
|||||||
|
|
||||||
Mathematical, isotropic surface providing equal strength in all directions. Excellent for strong, flexible prints and resin filling due to its interconnected structure. This pattern may require more time to slice because of all the points needed to generate each curve. If your model has complex geometry, consider using a simpler infill pattern like [TPMS-D](#tpms-d) or [Cross Hatch](#cross-hatch).
|
Mathematical, isotropic surface providing equal strength in all directions. Excellent for strong, flexible prints and resin filling due to its interconnected structure. This pattern may require more time to slice because of all the points needed to generate each curve. If your model has complex geometry, consider using a simpler infill pattern like [TPMS-D](#tpms-d) or [Cross Hatch](#cross-hatch).
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** High
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** High
|
- **Horizontal (X-Y):** 🟣 High
|
||||||
|
- **Vertical (Z):** 🟣 High
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-High
|
- **Print Time:** 🔴 Ultra-High
|
||||||
- **Material/Time (Higher better):** Normal-Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🔵 Unnoticeable
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
|
||||||
@@ -414,14 +501,17 @@ Mathematical, isotropic surface providing equal strength in all directions. Exce
|
|||||||
|
|
||||||
Fills the area with progressively smaller versions of the outer contour, creating a concentric pattern. Ideal for 100% infill or flexible prints.
|
Fills the area with progressively smaller versions of the outer contour, creating a concentric pattern. Ideal for 100% infill or flexible prints.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
- **[Ironing](quality_settings_ironing)**
|
- **[Ironing](quality_settings_ironing)**
|
||||||
|
|
||||||
@@ -429,17 +519,20 @@ Fills the area with progressively smaller versions of the outer contour, creatin
|
|||||||
|
|
||||||
## Hilbert Curve
|
## Hilbert Curve
|
||||||
|
|
||||||
Hilbert Curve is a space-filling curve that can be used to create a continuous infill pattern. It is known for its Aesthetic appeal and ability to fill space efficiently.
|
Hilbert Curve is a space-filling curve that can be used to create a continuous infill pattern. It is known for its aesthetic appeal and ability to fill space efficiently.
|
||||||
Print speed is very low due to the complexity of the path, which can lead to longer print times. It is not recommended for structural parts but can be used for Aesthetic purposes.
|
Print speed is very low due to the complexity of the path, which can lead to longer print times. It is not recommended for structural parts but can be used for aesthetic purposes.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** High
|
- **Print Time:** 🟠 Extra-High
|
||||||
- **Material/Time (Higher better):** Low
|
- **Material/Time (Higher better):** 🟡 Low
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
|
|
||||||

|

|
||||||
@@ -448,14 +541,17 @@ Print speed is very low due to the complexity of the path, which can lead to lon
|
|||||||
|
|
||||||
Spiral pattern that fills the area with concentric arcs, creating a smooth and continuous infill. Can be filled with resin thanks to its interconnected hollow structure, which allows the resin to flow through it and cure properly.
|
Spiral pattern that fills the area with concentric arcs, creating a smooth and continuous infill. Can be filled with resin thanks to its interconnected hollow structure, which allows the resin to flow through it and cure properly.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal-Low
|
- **Print Time:** 🔘 Normal-Low
|
||||||
- **Material/Time (Higher better):** Normal-High
|
- **Material/Time (Higher better):** 🔘 Normal-High
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
|
|
||||||

|

|
||||||
@@ -464,14 +560,17 @@ Spiral pattern that fills the area with concentric arcs, creating a smooth and c
|
|||||||
|
|
||||||
Aesthetic pattern with low strength and high print time.
|
Aesthetic pattern with low strength and high print time.
|
||||||
|
|
||||||
- **Horizontal Strength (X-Y):** Low
|
- **Strength**
|
||||||
- **Vertical Strength (Z):** Normal
|
- **Horizontal (X-Y):** 🟡 Low
|
||||||
|
- **Vertical (Z):** ⚪️ Normal
|
||||||
- **Density Calculation:** % of total infill volume
|
- **Density Calculation:** % of total infill volume
|
||||||
- **Material Usage:** Normal
|
- **Material Usage:** ⚪️ Normal
|
||||||
- **Print Time:** Normal
|
- **Print Time:** ⚪️ Normal
|
||||||
- **Material/Time (Higher better):** Normal-Low
|
- **Material/Time (Higher better):** ⚪️ Normal
|
||||||
|
- **Layer time Variability:** 🟢 None
|
||||||
- **Applies to:**
|
- **Applies to:**
|
||||||
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)** - **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
- **[Sparse Infill](strength_settings_infill#sparse-infill-density)**
|
||||||
|
- **[Solid Infill](strength_settings_infill#internal-solid-infill)**
|
||||||
- **[Surface](strength_settings_top_bottom_shells)**
|
- **[Surface](strength_settings_top_bottom_shells)**
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
|
#include "MaterialType.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@@ -590,13 +591,10 @@ double getadhesionCoeff(const PrintObject* printObject)
|
|||||||
for (auto iter = extrudersFirstLayer.begin(); iter != extrudersFirstLayer.end(); iter++) {
|
for (auto iter = extrudersFirstLayer.begin(); iter != extrudersFirstLayer.end(); iter++) {
|
||||||
if (modelVolume->extruder_id() == *iter) {
|
if (modelVolume->extruder_id() == *iter) {
|
||||||
if (Model::extruderParamsMap.find(modelVolume->extruder_id()) != Model::extruderParamsMap.end()) {
|
if (Model::extruderParamsMap.find(modelVolume->extruder_id()) != Model::extruderParamsMap.end()) {
|
||||||
if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "PETG" ||
|
std::string filament_type = Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName;
|
||||||
Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "PCTG") {
|
double adhesion_coefficient = 1.0; // Default value
|
||||||
adhesionCoeff = 2;
|
MaterialType::get_adhesion_coefficient(filament_type, adhesion_coefficient);
|
||||||
}
|
adhesionCoeff = adhesion_coefficient;
|
||||||
else if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "TPU") {
|
|
||||||
adhesionCoeff = 0.5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,6 +344,8 @@ set(lisbslic3r_sources
|
|||||||
PrintApply.cpp
|
PrintApply.cpp
|
||||||
PrintBase.cpp
|
PrintBase.cpp
|
||||||
PrintBase.hpp
|
PrintBase.hpp
|
||||||
|
MaterialType.cpp
|
||||||
|
MaterialType.hpp
|
||||||
PrintConfig.cpp
|
PrintConfig.cpp
|
||||||
PrintConfig.hpp
|
PrintConfig.hpp
|
||||||
Print.cpp
|
Print.cpp
|
||||||
|
|||||||
@@ -305,8 +305,8 @@ ConfigSubstitutions import_sla_archive(
|
|||||||
std::function<bool(int)> progr)
|
std::function<bool(int)> progr)
|
||||||
{
|
{
|
||||||
// Ensure minimum window size for marching squares
|
// Ensure minimum window size for marching squares
|
||||||
windowsize.x() = std::max(2, windowsize.x());
|
windowsize.x() = std::max(1, windowsize.x());
|
||||||
windowsize.y() = std::max(2, windowsize.y());
|
windowsize.y() = std::max(1, windowsize.y());
|
||||||
|
|
||||||
std::string exclude_entries{"thumbnail"};
|
std::string exclude_entries{"thumbnail"};
|
||||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||||
|
|||||||
@@ -1,49 +1,160 @@
|
|||||||
#ifndef MARCHINGSQUARES_HPP
|
#ifndef MARCHINGSQUARES_HPP
|
||||||
#define MARCHINGSQUARES_HPP
|
#define MARCHINGSQUARES_HPP
|
||||||
|
|
||||||
|
#include "Execution/ExecutionTBB.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
// Marching squares
|
||||||
|
//
|
||||||
|
// This algorithm generates 2D contour rings for a 2D scalar field (height
|
||||||
|
// map). See https://en.wikipedia.org/wiki/Marching_squares for details.
|
||||||
|
//
|
||||||
|
// This algorithm uses square cells (tiles) with corner tags that are set
|
||||||
|
// depending on which vertices are inside an ROI (higher than the selected
|
||||||
|
// isovalue threshold). We label the vertices counter-clockwise, and for ease
|
||||||
|
// of extraction from the m_tags bitmap we put them into a four bit bitmap in
|
||||||
|
// little-endian row-column order <cbda>;
|
||||||
|
//
|
||||||
|
// ^ u (up) ----> column "c" direction
|
||||||
|
// |
|
||||||
|
// a ----- d
|
||||||
|
// | | |
|
||||||
|
// l <--| |--> r | increasing row "r" direction
|
||||||
|
// (left) | | (right) \/
|
||||||
|
// b ----- c
|
||||||
|
// |
|
||||||
|
// \/ d (down)
|
||||||
|
//
|
||||||
|
// The m_tags bitmap has the top-left "a" corner of each cell packed
|
||||||
|
// into uint32_t values stored in little-endian order, with the "a" tag for
|
||||||
|
// the first cell (r=0,c=0) in the LSB bit0 of the first 32bit word of the
|
||||||
|
// first row. The grid has a one cell border round the raster area that is
|
||||||
|
// always clear so lines never extend out of the grid.
|
||||||
|
//
|
||||||
|
// From this grid of points we can get the <cbda> tags bitmap for the four
|
||||||
|
// corners of each cell with "a" in the LSB bit0 and "c" in bit3. Note the
|
||||||
|
// bits b,c,d are also the "a" bits of the cell's next-colum/row neighbour
|
||||||
|
// cells. Cells in the right-most column get their cleared right-side border
|
||||||
|
// <c_b_> tags by wrapping around the grid and getting the cleared values
|
||||||
|
// from the left-side border. Note the grid bitmap always includes at least
|
||||||
|
// one extra cleared boarder bit for the wrap-around of the last tag in the
|
||||||
|
// last cell. Cells in the bottom row get their cleared bottom-side border
|
||||||
|
// <cb__> tags by leaving them clear because their grid index exceeds the
|
||||||
|
// grid size.
|
||||||
|
//
|
||||||
|
// These tag bits are then translated into up/down/left/right cell-exit
|
||||||
|
// directions indicating the cell edge(s) that any lines will cross and exit
|
||||||
|
// the cell. As you walk counter-clock-wise around the cell corners in the
|
||||||
|
// order a->b->c->d, any edge from a unset-corner-tag to an set-corner-tag is
|
||||||
|
// where a line will cross, exiting the cell into the next cell.
|
||||||
|
//
|
||||||
|
// Note any set-to-unset-transition edge is where a line will enter a cell,
|
||||||
|
// but for marching the lines through the cells we only need to keep the next
|
||||||
|
// cell exit directions. The exit directions are stored as 4bit <urdl>
|
||||||
|
// left/down/right/up flags packed into uint32_t values in m_dirs, in
|
||||||
|
// counter-clockwise little-endian order.
|
||||||
|
//
|
||||||
|
// To build the line-rings we scan through the m_dirs directions for the
|
||||||
|
// cells until we find one with a set-edge. We then start a line and "march"
|
||||||
|
// it through the cells following the directions in each cell, recording each
|
||||||
|
// edge the line crosses that will become a point in the ring.
|
||||||
|
//
|
||||||
|
// Note that the outside-edge of the grid extends one cell past the raster
|
||||||
|
// grid, and this outer edge is always cleared. This means no line will cross
|
||||||
|
// out of the grid, and they will all eventually connect back to their
|
||||||
|
// starting cell to become a ring around an island of set tag bits, possibly
|
||||||
|
// containing rings around lakes of unset tag bits within them.
|
||||||
|
//
|
||||||
|
// As we march through the cells and build the lines, we clear the direction
|
||||||
|
// bits to indicate the edge-transition has been processed. After completing
|
||||||
|
// a ring we return to scanning for the next non-zeroed starting cell for the
|
||||||
|
// next ring. Eventually all rings will be found and completed, and the
|
||||||
|
// m_dirs map will be completely cleared.
|
||||||
|
//
|
||||||
|
// Note there are two ambigous cases when the diagonally opposite corner tags
|
||||||
|
// are set. In these cases two lines will enter and exit the cell. To avoid
|
||||||
|
// messy lines, we consistently choose the exit direction based on what edge
|
||||||
|
// the line entered the cell from. This also means when we start scanning for
|
||||||
|
// the next ring we should start checking at the same cell the last ring
|
||||||
|
// started from, because it could have a second ring passing through it.
|
||||||
|
//
|
||||||
namespace marchsq {
|
namespace marchsq {
|
||||||
|
|
||||||
// Marks a square in the grid
|
// Marks a square or point in grid or raster coordintes.
|
||||||
struct Coord {
|
struct Coord
|
||||||
|
{
|
||||||
long r = 0, c = 0;
|
long r = 0, c = 0;
|
||||||
|
|
||||||
Coord() = default;
|
Coord() = default;
|
||||||
explicit Coord(long s) : r(s), c(s) {}
|
explicit Coord(long s) : r(s), c(s) {}
|
||||||
Coord(long _r, long _c): r(_r), c(_c) {}
|
Coord(long _r, long _c) : r(_r), c(_c) {}
|
||||||
|
|
||||||
size_t seq(const Coord &res) const { return r * res.c + c; }
|
size_t seq(const Coord& res) const { return r * res.c + c; }
|
||||||
Coord& operator+=(const Coord& b) { r += b.r; c += b.c; return *this; }
|
Coord& operator+=(const Coord& b)
|
||||||
Coord operator+(const Coord& b) const { Coord a = *this; a += b; return a; }
|
{
|
||||||
|
r += b.r;
|
||||||
|
c += b.c;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Coord operator+(const Coord& b) const
|
||||||
|
{
|
||||||
|
Coord a = *this;
|
||||||
|
a += b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
bool operator==(const Coord& o) const { return (r == o.r) && (c == o.c); }
|
||||||
|
bool operator!=(const Coord& o) const { return !(*this == o); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Coord& o) { return os << "(r=" << o.r << ",c=" << o.c << ")"; }
|
||||||
|
|
||||||
// Closed ring of cell coordinates
|
// Closed ring of cell coordinates
|
||||||
using Ring = std::vector<Coord>;
|
using Ring = std::vector<Coord>;
|
||||||
|
|
||||||
// Specialize this struct to register a raster type for the Marching squares alg
|
inline std::ostream& operator<<(std::ostream& os, const Ring& r)
|
||||||
template<class T, class Enable = void> struct _RasterTraits {
|
{
|
||||||
|
os << "[" << r.size() << "]:";
|
||||||
|
for (Coord c : r)
|
||||||
|
os << " " << c;
|
||||||
|
return os << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specialize this struct to register a raster type for MarchingSquares.
|
||||||
|
template<class T, class Enable = void> struct _RasterTraits
|
||||||
|
{
|
||||||
// The type of pixel cell in the raster
|
// The type of pixel cell in the raster
|
||||||
using ValueType = typename T::ValueType;
|
using ValueType = typename T::ValueType;
|
||||||
|
|
||||||
// Value at a given position
|
// Value at a given position
|
||||||
static ValueType get(const T &raster, size_t row, size_t col);
|
static ValueType get(const T& raster, size_t row, size_t col);
|
||||||
|
|
||||||
// Number of rows and cols of the raster
|
// Number of rows and cols of the raster
|
||||||
static size_t rows(const T &raster);
|
static size_t rows(const T& raster);
|
||||||
static size_t cols(const T &raster);
|
static size_t cols(const T& raster);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Specialize this to use parellel loops within the algorithm
|
// Specialize this to use parellel loops within the algorithm
|
||||||
template<class ExecutionPolicy, class Enable = void> struct _Loop {
|
template<class ExecutionPolicy, class Enable = void> struct _Loop
|
||||||
template<class It, class Fn> static void for_each(It from, It to, Fn &&fn)
|
{
|
||||||
|
template<class It, class Fn> static void for_each_idx(It from, It to, Fn&& fn)
|
||||||
{
|
{
|
||||||
for (auto it = from; it < to; ++it) fn(*it, size_t(it - from));
|
for (auto it = from; it < to; ++it)
|
||||||
|
fn(*it, size_t(it - from));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add Specialization for using ExecutionTBB for parallel loops.
|
||||||
|
using namespace Slic3r;
|
||||||
|
template<> struct _Loop<ExecutionTBB>
|
||||||
|
{
|
||||||
|
template<class It, class Fn> static void for_each_idx(It from, It to, Fn&& fn)
|
||||||
|
{
|
||||||
|
execution::for_each(
|
||||||
|
ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, execution::max_concurrency(ex_tbb));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,388 +163,481 @@ namespace __impl {
|
|||||||
template<class T> using RasterTraits = _RasterTraits<std::decay_t<T>>;
|
template<class T> using RasterTraits = _RasterTraits<std::decay_t<T>>;
|
||||||
template<class T> using TRasterValue = typename RasterTraits<T>::ValueType;
|
template<class T> using TRasterValue = typename RasterTraits<T>::ValueType;
|
||||||
|
|
||||||
template<class T> size_t rows(const T &raster)
|
template<class T> size_t rows(const T& raster) { return RasterTraits<T>::rows(raster); }
|
||||||
|
|
||||||
|
template<class T> size_t cols(const T& raster) { return RasterTraits<T>::cols(raster); }
|
||||||
|
|
||||||
|
template<class T> TRasterValue<T> isoval(const T& rst, const Coord& crd) { return RasterTraits<T>::get(rst, crd.r, crd.c); }
|
||||||
|
|
||||||
|
template<class ExecutionPolicy, class It, class Fn> void for_each_idx(ExecutionPolicy&& policy, It from, It to, Fn&& fn)
|
||||||
{
|
{
|
||||||
return RasterTraits<T>::rows(raster);
|
_Loop<ExecutionPolicy>::for_each_idx(from, to, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T> size_t cols(const T &raster)
|
template<class E> constexpr std::underlying_type_t<E> _t(E e) noexcept { return static_cast<std::underlying_type_t<E>>(e); }
|
||||||
{
|
|
||||||
return RasterTraits<T>::cols(raster);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> TRasterValue<T> isoval(const T &rst, const Coord &crd)
|
// A set of next direction flags for a cell to indicate what sides(s)
|
||||||
{
|
// any yet-to-be-marched line(s) will cross to leave the cell.
|
||||||
return RasterTraits<T>::get(rst, crd.r, crd.c);
|
enum class Dir : uint8_t {
|
||||||
}
|
none = 0b0000,
|
||||||
|
left = 0b0001, /* exit through a-b edge */
|
||||||
template<class ExecutionPolicy, class It, class Fn>
|
down = 0b0010, /* exit through b-c edge */
|
||||||
void for_each(ExecutionPolicy&& policy, It from, It to, Fn &&fn)
|
right = 0b0100, /* exit through c-d edge */
|
||||||
{
|
leftright = left | right,
|
||||||
_Loop<ExecutionPolicy>::for_each(from, to, fn);
|
up = 0b1000, /* exit through d-a edge */
|
||||||
}
|
updown = up | down,
|
||||||
|
all = 0b1111
|
||||||
// Type of squares (tiles) depending on which vertices are inside an ROI
|
|
||||||
// The vertices would be marked a, b, c, d in counter clockwise order from the
|
|
||||||
// bottom left vertex of a square.
|
|
||||||
// d --- c
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// a --- b
|
|
||||||
enum class SquareTag : uint8_t {
|
|
||||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
|
||||||
none, a, b, ab, c, ac, bc, abc, d, ad, bd, abd, cd, acd, bcd, full
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class E> constexpr std::underlying_type_t<E> _t(E e) noexcept
|
inline std::ostream& operator<<(std::ostream& os, const Dir& d) { return os << ".<v#>x##^#X#####"[_t(d)]; }
|
||||||
|
|
||||||
|
// This maps square tag column/row order <cbda> bitmaps to the next
|
||||||
|
// direction(s) a line will exit the cell. The directions ensure the
|
||||||
|
// line-rings circle counter-clock-wise around the clusters of set tags.
|
||||||
|
static const constexpr std::array<uint32_t, 16> NEXT_CCW = [] {
|
||||||
|
auto map = decltype(NEXT_CCW){};
|
||||||
|
for (uint32_t cbda = 0; cbda < 16; cbda++) {
|
||||||
|
const uint32_t dcba = (cbda & 0b0001) | ((cbda >> 1) & 0b0110) | ((cbda << 2) & 0b1000);
|
||||||
|
const uint32_t adcb = (dcba >> 1) | ((dcba << 3) & 0b1000);
|
||||||
|
map[cbda] = ~dcba & adcb;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Step a point in a direction, optionally by n steps.
|
||||||
|
inline void step(Coord& crd, const Dir d, const long n = 1)
|
||||||
{
|
{
|
||||||
return static_cast<std::underlying_type_t<E>>(e);
|
switch (d) {
|
||||||
|
case Dir::left: crd.c -= n; break;
|
||||||
|
case Dir::down: crd.r += n; break;
|
||||||
|
case Dir::right: crd.c += n; break;
|
||||||
|
case Dir::up: crd.r -= n; break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Dir: uint8_t { left, down, right, up, none};
|
template<class Rst> class Grid
|
||||||
|
|
||||||
static const constexpr Dir NEXT_CCW[] = {
|
|
||||||
/* 00 */ Dir::none, // SquareTag::none (empty square, nowhere to go)
|
|
||||||
/* 01 */ Dir::left, // SquareTag::a
|
|
||||||
/* 02 */ Dir::down, // SquareTag::b
|
|
||||||
/* 03 */ Dir::left, // SquareTag::ab
|
|
||||||
/* 04 */ Dir::right, // SquareTag::c
|
|
||||||
/* 05 */ Dir::none, // SquareTag::ac (ambiguous case)
|
|
||||||
/* 06 */ Dir::down, // SquareTag::bc
|
|
||||||
/* 07 */ Dir::left, // SquareTag::abc
|
|
||||||
/* 08 */ Dir::up, // SquareTag::d
|
|
||||||
/* 09 */ Dir::up, // SquareTag::ad
|
|
||||||
/* 10 */ Dir::none, // SquareTag::bd (ambiguous case)
|
|
||||||
/* 11 */ Dir::up, // SquareTag::abd
|
|
||||||
/* 12 */ Dir::right, // SquareTag::cd
|
|
||||||
/* 13 */ Dir::right, // SquareTag::acd
|
|
||||||
/* 14 */ Dir::down, // SquareTag::bcd
|
|
||||||
/* 15 */ Dir::none // SquareTag::full (full covered, nowhere to go)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const constexpr uint8_t PREV_CCW[] = {
|
|
||||||
/* 00 */ 1 << _t(Dir::none),
|
|
||||||
/* 01 */ 1 << _t(Dir::up),
|
|
||||||
/* 02 */ 1 << _t(Dir::left),
|
|
||||||
/* 03 */ 1 << _t(Dir::left),
|
|
||||||
/* 04 */ 1 << _t(Dir::down),
|
|
||||||
/* 05 */ 1 << _t(Dir::up) | 1 << _t(Dir::down),
|
|
||||||
/* 06 */ 1 << _t(Dir::down),
|
|
||||||
/* 07 */ 1 << _t(Dir::down),
|
|
||||||
/* 08 */ 1 << _t(Dir::right),
|
|
||||||
/* 09 */ 1 << _t(Dir::up),
|
|
||||||
/* 10 */ 1 << _t(Dir::left) | 1 << _t(Dir::right),
|
|
||||||
/* 11 */ 1 << _t(Dir::left),
|
|
||||||
/* 12 */ 1 << _t(Dir::right),
|
|
||||||
/* 13 */ 1 << _t(Dir::up),
|
|
||||||
/* 14 */ 1 << _t(Dir::right),
|
|
||||||
/* 15 */ 1 << _t(Dir::none)
|
|
||||||
};
|
|
||||||
|
|
||||||
const constexpr uint8_t DIRMASKS[] = {
|
|
||||||
/*left: */ 0x01, /*down*/ 0x12, /*right */0x21, /*up*/ 0x10, /*none*/ 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Coord step(const Coord &crd, Dir d)
|
|
||||||
{
|
{
|
||||||
uint8_t dd = DIRMASKS[uint8_t(d)];
|
const Rst* m_rst = nullptr;
|
||||||
return {crd.r - 1 + (dd & 0x0f), crd.c - 1 + (dd >> 4)};
|
Coord m_window, m_rastsize, m_gridsize;
|
||||||
}
|
size_t m_gridlen; // The number of cells in the grid.
|
||||||
|
std::vector<uint32_t> m_tags; // bit-packed squaretags for each corner.
|
||||||
|
std::vector<uint32_t> m_dirs; // bit-packed next directions for each cell.
|
||||||
|
|
||||||
template<class Rst> class Grid {
|
// Convert a grid coordinate point into raster coordinates.
|
||||||
const Rst * m_rst = nullptr;
|
inline Coord rastercoord(const Coord& crd) const
|
||||||
Coord m_cellsize, m_res_1, m_window, m_gridsize, m_grid_1;
|
|
||||||
std::vector<uint8_t> m_tags; // Assign tags to each square
|
|
||||||
|
|
||||||
Coord rastercoord(const Coord &crd) const
|
|
||||||
{
|
{
|
||||||
|
// Note the -1 offset for the grid border around the raster area.
|
||||||
return {(crd.r - 1) * m_window.r, (crd.c - 1) * m_window.c};
|
return {(crd.r - 1) * m_window.r, (crd.c - 1) * m_window.c};
|
||||||
}
|
}
|
||||||
|
|
||||||
Coord bl(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, 0}; }
|
// Get the 4 corners of a grid coordinate cell in raster coordinates.
|
||||||
Coord br(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, m_res_1.c}; }
|
Coord bl(const Coord& crd) const { return tl(crd) + Coord{m_window.r, 0}; }
|
||||||
Coord tr(const Coord &crd) const { return tl(crd) + Coord{0, m_res_1.c}; }
|
Coord br(const Coord& crd) const { return tl(crd) + Coord{m_window.r, m_window.c}; }
|
||||||
Coord tl(const Coord &crd) const { return rastercoord(crd); }
|
Coord tr(const Coord& crd) const { return tl(crd) + Coord{0, m_window.c}; }
|
||||||
|
Coord tl(const Coord& crd) const { return rastercoord(crd); }
|
||||||
bool is_within(const Coord &crd)
|
|
||||||
{
|
|
||||||
long R = rows(*m_rst), C = cols(*m_rst);
|
|
||||||
return crd.r >= 0 && crd.r < R && crd.c >= 0 && crd.c < C;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate the tag for a cell (or square). The cell coordinates mark the
|
// Test if a raster coordinate point is within the raster area.
|
||||||
// top left vertex of a square in the raster. v is the isovalue
|
inline bool is_within(const Coord& crd) const { return crd.r >= 0 && crd.r < m_rastsize.r && crd.c >= 0 && crd.c < m_rastsize.c; }
|
||||||
uint8_t get_tag_for_cell(const Coord &cell, TRasterValue<Rst> v)
|
|
||||||
{
|
|
||||||
Coord sqr[] = {bl(cell), br(cell), tr(cell), tl(cell)};
|
|
||||||
|
|
||||||
uint8_t t = ((is_within(sqr[0]) && isoval(*m_rst, sqr[0]) >= v)) +
|
|
||||||
((is_within(sqr[1]) && isoval(*m_rst, sqr[1]) >= v) << 1) +
|
|
||||||
((is_within(sqr[2]) && isoval(*m_rst, sqr[2]) >= v) << 2) +
|
|
||||||
((is_within(sqr[3]) && isoval(*m_rst, sqr[3]) >= v) << 3);
|
|
||||||
|
|
||||||
assert(t < 16);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a cell coordinate from a sequential index
|
|
||||||
Coord coord(size_t i) const
|
|
||||||
{
|
|
||||||
return {long(i) / m_gridsize.c, long(i) % m_gridsize.c};
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t seq(const Coord &crd) const { return crd.seq(m_gridsize); }
|
// Get a block of 32 tags for a block index from the raster isovals.
|
||||||
|
uint32_t get_tags_block32(const size_t bidx, const TRasterValue<Rst> v) const
|
||||||
bool is_visited(size_t idx, Dir d = Dir::none) const
|
|
||||||
{
|
{
|
||||||
SquareTag t = get_tag(idx);
|
Coord gcrd = coord(bidx * 32); // position in grid coordinates of the block.
|
||||||
uint8_t ref = d == Dir::none ? PREV_CCW[_t(t)] : uint8_t(1 << _t(d));
|
Coord rcrd = rastercoord(gcrd); // position in raster coordinates.
|
||||||
return t == SquareTag::full || t == SquareTag::none ||
|
uint32_t tags = 0;
|
||||||
((m_tags[idx] & 0xf0) >> 4) == ref;
|
// Set a bit for each corner that has osoval > v.
|
||||||
}
|
for (uint32_t b = 1; b; b <<= 1) {
|
||||||
|
if (is_within(rcrd) && isoval(*m_rst, rcrd) > v)
|
||||||
void set_visited(size_t idx, Dir d = Dir::none)
|
tags |= b;
|
||||||
{
|
gcrd.c += 1;
|
||||||
m_tags[idx] |= (1 << (_t(d)) << 4);
|
rcrd.c += m_window.c;
|
||||||
}
|
// If we hit the end of the row, start on the next row.
|
||||||
|
if (gcrd.c >= m_gridsize.c) {
|
||||||
bool is_ambiguous(size_t idx) const
|
gcrd = Coord(gcrd.r + 1, 0);
|
||||||
{
|
rcrd = rastercoord(gcrd);
|
||||||
SquareTag t = get_tag(idx);
|
|
||||||
return t == SquareTag::ac || t == SquareTag::bd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for a new starting square
|
|
||||||
size_t search_start_cell(size_t i = 0) const
|
|
||||||
{
|
|
||||||
// Skip ambiguous tags as starting tags due to unknown previous
|
|
||||||
// direction.
|
|
||||||
while ((i < m_tags.size()) && (is_visited(i) || is_ambiguous(i))) ++i;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
SquareTag get_tag(size_t idx) const { return SquareTag(m_tags[idx] & 0x0f); }
|
|
||||||
|
|
||||||
Dir next_dir(Dir prev, SquareTag tag) const
|
|
||||||
{
|
|
||||||
// Treat ambiguous cases as two separate regions in one square.
|
|
||||||
switch (tag) {
|
|
||||||
case SquareTag::ac:
|
|
||||||
switch (prev) {
|
|
||||||
case Dir::down: return Dir::right;
|
|
||||||
case Dir::up: return Dir::left;
|
|
||||||
default: assert(false); return Dir::none;
|
|
||||||
}
|
}
|
||||||
case SquareTag::bd:
|
|
||||||
switch (prev) {
|
|
||||||
case Dir::right: return Dir::up;
|
|
||||||
case Dir::left: return Dir::down;
|
|
||||||
default: assert(false); return Dir::none;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return NEXT_CCW[uint8_t(tag)];
|
|
||||||
}
|
}
|
||||||
|
return tags;
|
||||||
return Dir::none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CellIt {
|
// Get a block of 8 directions for a block index from the tags.
|
||||||
Coord crd; Dir dir= Dir::none; const Rst *grid = nullptr;
|
uint32_t get_dirs_block8(const size_t bidx) const
|
||||||
|
{
|
||||||
TRasterValue<Rst> operator*() const { return isoval(*grid, crd); }
|
size_t gidx = bidx * 8;
|
||||||
CellIt& operator++() { crd = step(crd, dir); return *this; }
|
uint32_t dirs = 0;
|
||||||
CellIt operator++(int) { CellIt it = *this; ++(*this); return it; }
|
|
||||||
bool operator!=(const CellIt &it) { return crd.r != it.crd.r || crd.c != it.crd.c; }
|
// Get the next 9 top-row tags at this grid index into the bottom 16
|
||||||
|
// bits, and the 9 bottom-row tags into the top 16 bits.
|
||||||
|
uint32_t tags9 = get_tags9(gidx) | (get_tags9(gidx + m_gridsize.c) << 16);
|
||||||
|
// Skip generating dirs if the tags are all 1's or all 0's.
|
||||||
|
if ((tags9 != 0) && (tags9 != 0x01ff01ff)) {
|
||||||
|
for (auto s = 0; s < 32; s += 4) {
|
||||||
|
uint8_t tags = (tags9 & 0b11) | ((tags9 >> 14) & 0b1100);
|
||||||
|
dirs |= NEXT_CCW[tags] << s;
|
||||||
|
tags9 >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next 9 corner tags on a row for building a dirs block at a grid index.
|
||||||
|
uint32_t get_tags9(size_t gidx) const
|
||||||
|
{
|
||||||
|
uint32_t tags = 0; // the tags value.
|
||||||
|
size_t i = gidx / 32; // the tags block index
|
||||||
|
int o = gidx % 32; // the tags block offset
|
||||||
|
|
||||||
|
if (gidx < m_gridlen) {
|
||||||
|
// get the next 9 tags in the row.
|
||||||
|
tags = (m_tags[i] >> o) & 0x1ff;
|
||||||
|
// Some of the tags are in the next tags block.
|
||||||
|
if ((o > (32 - 9)) && ((i + 1) < m_tags.size())) {
|
||||||
|
tags |= (m_tags[i + 1] << (32 - o)) & 0x1ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear directions in a cell's <urdl> dirs for a grid index.
|
||||||
|
void clr_dirs(const size_t gidx, const Dir d)
|
||||||
|
{
|
||||||
|
assert(gidx < m_gridlen);
|
||||||
|
size_t i = gidx / 8; // the dirs block index
|
||||||
|
int o = (gidx % 8); // the dirs block offset
|
||||||
|
|
||||||
|
m_dirs[i] &= ~(static_cast<uint32_t>(d) << (o * 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get directions in a cell's <uldr> dirs from the m_dirs store.
|
||||||
|
Dir get_dirs(const size_t gidx) const
|
||||||
|
{
|
||||||
|
assert(gidx < m_gridlen);
|
||||||
|
size_t i = gidx / 8; // the dirs block index
|
||||||
|
int o = (gidx % 8); // the dirs block offset
|
||||||
|
|
||||||
|
return Dir((m_dirs[i] >> (o * 4)) & 0b1111);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a cell coordinate from a sequential grid index.
|
||||||
|
Coord coord(size_t i) const { return {long(i) / m_gridsize.c, long(i) % m_gridsize.c}; }
|
||||||
|
|
||||||
|
// Get a sequential index from a cell coordinate.
|
||||||
|
size_t seq(const Coord& crd) const { return crd.seq(m_gridsize); }
|
||||||
|
|
||||||
|
// Step a sequential grid index in a direction.
|
||||||
|
size_t stepidx(const size_t idx, const Dir d) const
|
||||||
|
{
|
||||||
|
assert(idx < m_gridlen);
|
||||||
|
switch (d) {
|
||||||
|
case Dir::left: return idx - 1;
|
||||||
|
case Dir::down: return idx + m_gridsize.c;
|
||||||
|
case Dir::right: return idx + 1;
|
||||||
|
case Dir::up: return idx - m_gridsize.c;
|
||||||
|
default: return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a new starting square.
|
||||||
|
size_t search_start_cell(size_t gidx = 0) const
|
||||||
|
{
|
||||||
|
size_t i = gidx / 8; // The dirs block index.
|
||||||
|
int o = gidx % 8; // The dirs block offset.
|
||||||
|
|
||||||
|
// Skip cells without any unvisited edges.
|
||||||
|
while (i < m_dirs.size()) {
|
||||||
|
if (!m_dirs[i]) {
|
||||||
|
// Whole block is clear, advance to the next block;
|
||||||
|
i++;
|
||||||
|
o = 0;
|
||||||
|
} else {
|
||||||
|
// Block not clear, find the next non-zero tags in the block. Note
|
||||||
|
// all dirs before gidx are cleared, so any set bits must be in
|
||||||
|
// block offsets >= o, not before.
|
||||||
|
for (uint32_t m = 0b1111 << (o * 4); !(m_dirs[i] & m); m <<= 4)
|
||||||
|
o++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i * 8 + o;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next direction for a cell index after the prev direction.
|
||||||
|
Dir next_dir(size_t idx, Dir prev = Dir::all) const
|
||||||
|
{
|
||||||
|
Dir next = get_dirs(idx);
|
||||||
|
|
||||||
|
// Treat ambiguous cases as two separate regions in one square. If
|
||||||
|
// there are two possible next directions, pick based on the prev
|
||||||
|
// direction. If prev=all we are starting a new line so pick the one
|
||||||
|
// that leads "forwards" (right or down) in the search.
|
||||||
|
switch (next) {
|
||||||
|
case Dir::leftright:
|
||||||
|
// We must be coming from up, down, or starting a new line.
|
||||||
|
assert(prev == Dir::up || prev == Dir::down || prev == Dir::all);
|
||||||
|
return (prev == Dir::up) ? Dir::left : Dir::right;
|
||||||
|
case Dir::updown:
|
||||||
|
// We must be coming from left, right, or starting a new line.
|
||||||
|
assert(prev == Dir::left || prev == Dir::right || prev == Dir::all);
|
||||||
|
return (prev == Dir::right) ? Dir::up : Dir::down;
|
||||||
|
default:
|
||||||
|
// Next must be a single direction or none to stop.
|
||||||
|
assert(next == Dir::none || next == Dir::left || next == Dir::down || next == Dir::right || next == Dir::up);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CellIt
|
||||||
|
{
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
using value_type = TRasterValue<Rst>;
|
using value_type = TRasterValue<Rst>;
|
||||||
using pointer = TRasterValue<Rst> *;
|
using pointer = TRasterValue<Rst>*;
|
||||||
using reference = TRasterValue<Rst> &;
|
using reference = TRasterValue<Rst>&;
|
||||||
using difference_type = long;
|
using difference_type = long;
|
||||||
using iterator_category = std::forward_iterator_tag;
|
|
||||||
|
Coord crd;
|
||||||
|
Dir dir = Dir::none;
|
||||||
|
const Rst* grid = nullptr;
|
||||||
|
|
||||||
|
inline TRasterValue<Rst> operator*() const { return isoval(*grid, crd); }
|
||||||
|
inline TRasterValue<Rst>& operator[](long n) const { return *((*this) + n); }
|
||||||
|
inline CellIt& operator++()
|
||||||
|
{
|
||||||
|
step(crd, dir);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline CellIt operator++(int)
|
||||||
|
{
|
||||||
|
CellIt o = *this;
|
||||||
|
++(*this);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
inline CellIt& operator--()
|
||||||
|
{
|
||||||
|
step(crd, dir, -1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline CellIt operator--(int)
|
||||||
|
{
|
||||||
|
CellIt o = *this;
|
||||||
|
--(*this);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
inline CellIt& operator+=(long n)
|
||||||
|
{
|
||||||
|
step(crd, dir, n);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline CellIt& operator-=(long n)
|
||||||
|
{
|
||||||
|
step(crd, dir, -n);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline CellIt operator+(long n) const
|
||||||
|
{
|
||||||
|
CellIt o = *this;
|
||||||
|
o += n;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
inline CellIt operator-(long n) const
|
||||||
|
{
|
||||||
|
CellIt o = *this;
|
||||||
|
o -= n;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
inline long operator-(const CellIt& o) const
|
||||||
|
{
|
||||||
|
switch (dir) {
|
||||||
|
case Dir::left: return o.crd.c - crd.c;
|
||||||
|
case Dir::down: return crd.r - o.crd.r;
|
||||||
|
case Dir::right: return crd.c - o.crd.c;
|
||||||
|
case Dir::up: return o.crd.r - crd.r;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline bool operator==(const CellIt& o) const { return crd == o.crd; }
|
||||||
|
inline bool operator!=(const CellIt& o) const { return crd != o.crd; }
|
||||||
|
inline bool operator<(const CellIt& o) const { return (*this - o) < 0; }
|
||||||
|
inline bool operator>(const CellIt& o) const { return (*this - o) > 0; }
|
||||||
|
inline bool operator<=(const CellIt& o) const { return (*this - o) <= 0; }
|
||||||
|
inline bool operator>=(const CellIt& o) const { return (*this - o) >= 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Two cell iterators representing an edge of a square. This is then
|
// Two cell iterators representing an edge of a square. This is then
|
||||||
// used for binary search for the first active pixel on the edge.
|
// used for binary search for the first active pixel on the edge.
|
||||||
struct Edge { CellIt from, to; };
|
struct Edge
|
||||||
|
|
||||||
Edge _edge(const Coord &ringvertex) const
|
|
||||||
{
|
{
|
||||||
size_t idx = ringvertex.r;
|
CellIt from, to;
|
||||||
Coord cell = coord(idx);
|
};
|
||||||
uint8_t tg = m_tags[ringvertex.r];
|
|
||||||
SquareTag t = SquareTag(tg & 0x0f);
|
Edge _edge(const Coord& ringvertex) const
|
||||||
|
{
|
||||||
switch (t) {
|
size_t idx = ringvertex.r;
|
||||||
case SquareTag::a:
|
Coord cell = coord(idx);
|
||||||
case SquareTag::ab:
|
Dir d = Dir(ringvertex.c);
|
||||||
case SquareTag::abc:
|
|
||||||
return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
|
switch (d) {
|
||||||
case SquareTag::b:
|
case Dir::left: return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
|
||||||
case SquareTag::bc:
|
case Dir::down: return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
|
||||||
case SquareTag::bcd:
|
case Dir::right: return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
|
||||||
return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
|
case Dir::up: return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
|
||||||
case SquareTag::c:
|
default: assert(false);
|
||||||
return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
|
|
||||||
case SquareTag::ac:
|
|
||||||
switch (Dir(ringvertex.c)) {
|
|
||||||
case Dir::left: return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
|
|
||||||
case Dir::right: return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
|
|
||||||
default: assert(false);
|
|
||||||
}
|
|
||||||
case SquareTag::d:
|
|
||||||
case SquareTag::ad:
|
|
||||||
case SquareTag::abd:
|
|
||||||
return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
|
|
||||||
case SquareTag::bd:
|
|
||||||
switch (Dir(ringvertex.c)) {
|
|
||||||
case Dir::down: return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
|
|
||||||
case Dir::up: return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
|
|
||||||
default: assert(false);
|
|
||||||
}
|
|
||||||
case SquareTag::cd:
|
|
||||||
case SquareTag::acd:
|
|
||||||
return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
|
|
||||||
case SquareTag::full:
|
|
||||||
case SquareTag::none: {
|
|
||||||
Coord crd{tl(cell) + Coord{m_cellsize.r / 2, m_cellsize.c / 2}};
|
|
||||||
return {{crd, Dir::none, m_rst}, {crd}};
|
|
||||||
}
|
}
|
||||||
}
|
return {};
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Edge edge(const Coord &ringvertex) const
|
Edge edge(const Coord& ringvertex) const
|
||||||
{
|
{
|
||||||
const long R = rows(*m_rst), C = cols(*m_rst);
|
Edge e = _edge(ringvertex);
|
||||||
const long R_1 = R - 1, C_1 = C - 1;
|
|
||||||
|
|
||||||
Edge e = _edge(ringvertex);
|
|
||||||
e.to.dir = e.from.dir;
|
e.to.dir = e.from.dir;
|
||||||
++e.to;
|
++e.to;
|
||||||
|
|
||||||
e.from.crd.r = std::min(e.from.crd.r, R_1);
|
e.from.crd.r = std::clamp(e.from.crd.r, 0l, m_rastsize.r - 1);
|
||||||
e.from.crd.r = std::max(e.from.crd.r, 0l);
|
e.from.crd.c = std::clamp(e.from.crd.c, 0l, m_rastsize.c - 1);
|
||||||
e.from.crd.c = std::min(e.from.crd.c, C_1);
|
e.to.crd.r = std::clamp(e.to.crd.r, 0l, m_rastsize.r);
|
||||||
e.from.crd.c = std::max(e.from.crd.c, 0l);
|
e.to.crd.c = std::clamp(e.to.crd.c, 0l, m_rastsize.c);
|
||||||
|
|
||||||
e.to.crd.r = std::min(e.to.crd.r, R);
|
|
||||||
e.to.crd.r = std::max(e.to.crd.r, 0l);
|
|
||||||
e.to.crd.c = std::min(e.to.crd.c, C);
|
|
||||||
e.to.crd.c = std::max(e.to.crd.c, 0l);
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
void interpolate_edge(Coord& ecrd, TRasterValue<Rst> isoval) const
|
||||||
explicit Grid(const Rst &rst, const Coord &cellsz, const Coord &overlap)
|
{
|
||||||
: m_rst{&rst}
|
// The ecrd must have a grid index in r and a direction in c.
|
||||||
, m_cellsize{cellsz}
|
assert((0 <= ecrd.r) && (ecrd.r < m_gridlen));
|
||||||
, m_res_1{m_cellsize.r - 1, m_cellsize.c - 1}
|
assert((ecrd.c == long(Dir::left)) || (ecrd.c == long(Dir::down)) || (ecrd.c == long(Dir::right)) || (ecrd.c == long(Dir::up)));
|
||||||
, m_window{overlap.r < cellsz.r ? cellsz.r - overlap.r : cellsz.r,
|
Edge e = edge(ecrd);
|
||||||
overlap.c < cellsz.c ? cellsz.c - overlap.c : cellsz.c}
|
ecrd = std::lower_bound(e.from, e.to, isoval).crd;
|
||||||
, m_gridsize{2 + (long(rows(rst)) - overlap.r) / m_window.r,
|
// Shift bottom and right side points "out" by one to account for
|
||||||
2 + (long(cols(rst)) - overlap.c) / m_window.c}
|
// raster pixel width. Note "dir" is the direction of interpolation
|
||||||
, m_tags(m_gridsize.r * m_gridsize.c, 0)
|
// along the cell edge, not the next move direction.
|
||||||
{}
|
if (e.from.dir == Dir::up)
|
||||||
|
ecrd.r += 1;
|
||||||
// Go through the cells and mark them with the appropriate tag.
|
else if (e.from.dir == Dir::left)
|
||||||
template<class ExecutionPolicy>
|
ecrd.c += 1;
|
||||||
void tag_grid(ExecutionPolicy &&policy, TRasterValue<Rst> isoval)
|
|
||||||
{
|
|
||||||
// parallel for r
|
|
||||||
for_each (std::forward<ExecutionPolicy>(policy),
|
|
||||||
m_tags.begin(), m_tags.end(),
|
|
||||||
[this, isoval](uint8_t& tag, size_t idx) {
|
|
||||||
tag = get_tag_for_cell(coord(idx), isoval);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan for the rings on the tagged grid. Each ring vertex stores the
|
public:
|
||||||
// sequential index of the cell and the next direction (Dir).
|
explicit Grid(const Rst& rst, const Coord& window)
|
||||||
// This info can be used later to calculate the exact raster coordinate.
|
: m_rst{&rst}
|
||||||
|
, m_window{window}
|
||||||
|
, m_rastsize{static_cast<long>(rows(rst)), static_cast<long>(cols(rst))}
|
||||||
|
, m_gridsize{2 + m_rastsize.r / m_window.r, 2 + m_rastsize.c / m_window.c}
|
||||||
|
, m_gridlen{static_cast<size_t>(m_gridsize.r * m_gridsize.c)}
|
||||||
|
, m_tags(m_gridlen / 32 + 1, 0) // 1 bit per tag means 32 per uint32_t.
|
||||||
|
, m_dirs(m_gridlen / 8 + 1, 0) // 4 bits per cell means 32/4=8 per uint32_t.
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Go through the cells getting their tags and dirs.
|
||||||
|
template<class ExecutionPolicy> void tag_grid(ExecutionPolicy&& policy, TRasterValue<Rst> isoval)
|
||||||
|
{
|
||||||
|
// Get all the tags. parallel?
|
||||||
|
for_each_idx(std::forward<ExecutionPolicy>(policy), m_tags.begin(), m_tags.end(),
|
||||||
|
[this, isoval](uint32_t& tag_block, size_t bidx) { tag_block = get_tags_block32(bidx, isoval); });
|
||||||
|
// streamtags(std::cerr);
|
||||||
|
// Get all the dirs. parallel?
|
||||||
|
for_each_idx(std::forward<ExecutionPolicy>(policy), m_dirs.begin(), m_dirs.end(),
|
||||||
|
[this](uint32_t& dirs_block, size_t bidx) { dirs_block = get_dirs_block8(bidx); });
|
||||||
|
// streamdirs(std::cerr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for the rings on the tagged grid. Each ring vertex uses the Coord
|
||||||
|
// to store the sequential cell index (idx in r) and next direction (Dir
|
||||||
|
// in c) for the next point of the ring. This info can be used later to
|
||||||
|
// calculate the exact raster coordinate of the point.
|
||||||
std::vector<Ring> scan_rings()
|
std::vector<Ring> scan_rings()
|
||||||
{
|
{
|
||||||
std::vector<Ring> rings;
|
std::vector<Ring> rings;
|
||||||
size_t startidx = 0;
|
size_t startidx = 0;
|
||||||
while ((startidx = search_start_cell(startidx)) < m_tags.size()) {
|
while ((startidx = search_start_cell(startidx)) < m_gridlen) {
|
||||||
Ring ring;
|
Ring ring;
|
||||||
|
|
||||||
size_t idx = startidx;
|
size_t idx = startidx;
|
||||||
Dir prev = Dir::none, next = next_dir(prev, get_tag(idx));
|
Dir next = next_dir(idx);
|
||||||
|
do {
|
||||||
while (next != Dir::none && !is_visited(idx, prev)) {
|
// We should never touch a cell with no remaining directions
|
||||||
|
// until we get back to the start cell.
|
||||||
|
assert(next != Dir::none);
|
||||||
Coord ringvertex{long(idx), long(next)};
|
Coord ringvertex{long(idx), long(next)};
|
||||||
ring.emplace_back(ringvertex);
|
ring.emplace_back(ringvertex);
|
||||||
set_visited(idx, prev);
|
clr_dirs(idx, next);
|
||||||
|
idx = stepidx(idx, next);
|
||||||
idx = seq(step(coord(idx), next));
|
next = next_dir(idx, next);
|
||||||
prev = next;
|
assert(idx < m_gridlen);
|
||||||
next = next_dir(next, get_tag(idx));
|
} while (idx != startidx);
|
||||||
}
|
// The start cell on returning should either have its directions
|
||||||
|
// cleared or have the second ambiguous direction.
|
||||||
// To prevent infinite loops in case of degenerate input
|
assert(next == Dir::none || next == Dir::left || next == Dir::up);
|
||||||
if (next == Dir::none) m_tags[startidx] = _t(SquareTag::none);
|
if (ring.size() > 1)
|
||||||
|
|
||||||
if (ring.size() > 1) {
|
|
||||||
ring.pop_back();
|
|
||||||
rings.emplace_back(ring);
|
rings.emplace_back(ring);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rings;
|
return rings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the exact raster position from the cells which store the
|
// Calculate the exact raster position from the cells which store the
|
||||||
// sequantial index of the square and the next direction
|
// sequential index of the square and the next direction
|
||||||
template<class ExecutionPolicy>
|
template<class ExecutionPolicy> void interpolate_rings(ExecutionPolicy&& policy, std::vector<Ring>& rings, TRasterValue<Rst> isov)
|
||||||
void interpolate_rings(ExecutionPolicy && policy,
|
|
||||||
std::vector<Ring> &rings,
|
|
||||||
TRasterValue<Rst> isov)
|
|
||||||
{
|
{
|
||||||
for_each(std::forward<ExecutionPolicy>(policy),
|
for_each_idx(std::forward<ExecutionPolicy>(policy), rings.begin(), rings.end(), [this, isov](Ring& ring, size_t) {
|
||||||
rings.begin(), rings.end(), [this, isov] (Ring &ring, size_t)
|
for (Coord& e : ring)
|
||||||
{
|
interpolate_edge(e, isov);
|
||||||
for (Coord &ringvertex : ring) {
|
|
||||||
Edge e = edge(ringvertex);
|
|
||||||
|
|
||||||
CellIt found = std::lower_bound(e.from, e.to, isov);
|
|
||||||
ringvertex = found.crd;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& streamtags(std::ostream& os)
|
||||||
|
{
|
||||||
|
os << " :";
|
||||||
|
for (auto c = 0; c < m_gridsize.c; c++)
|
||||||
|
os << (c % 10);
|
||||||
|
os << "\n";
|
||||||
|
for (auto r = 0; r < m_gridsize.r; r++) {
|
||||||
|
os << std::setw(3) << r << ":";
|
||||||
|
for (auto c = 0; c < m_gridsize.c; c++)
|
||||||
|
os << ((get_tags9(seq(Coord(r, c))) & 1) ? "H" : ".");
|
||||||
|
os << "\n";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& streamdirs(std::ostream& os)
|
||||||
|
{
|
||||||
|
os << " :";
|
||||||
|
for (auto c = 0; c < m_gridsize.c; c++)
|
||||||
|
os << (c % 10);
|
||||||
|
os << "\n";
|
||||||
|
for (auto r = 0; r < m_gridsize.r; r++) {
|
||||||
|
os << std::setw(3) << r << ":";
|
||||||
|
for (auto c = 0; c < m_gridsize.c; c++)
|
||||||
|
os << get_dirs(seq(Coord(r, c)));
|
||||||
|
os << std::endl;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Raster, class ExecutionPolicy>
|
template<class Raster, class ExecutionPolicy>
|
||||||
std::vector<marchsq::Ring> execute_with_policy(ExecutionPolicy && policy,
|
std::vector<marchsq::Ring> execute_with_policy(ExecutionPolicy&& policy,
|
||||||
const Raster & raster,
|
const Raster& raster,
|
||||||
TRasterValue<Raster> isoval,
|
TRasterValue<Raster> isoval,
|
||||||
Coord windowsize = {})
|
Coord windowsize = {})
|
||||||
{
|
{
|
||||||
if (!rows(raster) || !cols(raster)) return {};
|
if (!rows(raster) || !cols(raster))
|
||||||
|
return {};
|
||||||
|
|
||||||
size_t ratio = cols(raster) / rows(raster);
|
size_t ratio = cols(raster) / rows(raster);
|
||||||
|
|
||||||
if (!windowsize.r) windowsize.r = 2;
|
if (!windowsize.r)
|
||||||
|
windowsize.r = 2;
|
||||||
if (!windowsize.c)
|
if (!windowsize.c)
|
||||||
windowsize.c = std::max(2l, long(windowsize.r * ratio));
|
windowsize.c = std::max(2l, long(windowsize.r * ratio));
|
||||||
|
|
||||||
Coord overlap{1};
|
Grid<Raster> grid{raster, windowsize};
|
||||||
|
|
||||||
Grid<Raster> grid{raster, windowsize, overlap};
|
|
||||||
|
|
||||||
grid.tag_grid(std::forward<ExecutionPolicy>(policy), isoval);
|
grid.tag_grid(std::forward<ExecutionPolicy>(policy), isoval);
|
||||||
std::vector<marchsq::Ring> rings = grid.scan_rings();
|
std::vector<marchsq::Ring> rings = grid.scan_rings();
|
||||||
grid.interpolate_rings(std::forward<ExecutionPolicy>(policy), rings, isoval);
|
grid.interpolate_rings(std::forward<ExecutionPolicy>(policy), rings, isoval);
|
||||||
|
|
||||||
return rings;
|
return rings;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Raster>
|
template<class Raster> std::vector<marchsq::Ring> execute(const Raster& raster, TRasterValue<Raster> isoval, Coord windowsize = {})
|
||||||
std::vector<marchsq::Ring> execute(const Raster &raster,
|
|
||||||
TRasterValue<Raster> isoval,
|
|
||||||
Coord windowsize = {})
|
|
||||||
{
|
{
|
||||||
return execute_with_policy(nullptr, raster, isoval, windowsize);
|
return execute_with_policy(nullptr, raster, isoval, windowsize);
|
||||||
}
|
}
|
||||||
|
|||||||
169
src/libslic3r/MaterialType.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#include "MaterialType.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace {
|
||||||
|
constexpr int DEFAULT_MIN_TEMP = 190;
|
||||||
|
constexpr int DEFAULT_MAX_TEMP = 300;
|
||||||
|
constexpr int DEFAULT_CHAMBER_MIN_TEMP = 0;
|
||||||
|
constexpr int DEFAULT_CHAMBER_MAX_TEMP = 100;
|
||||||
|
constexpr double DEFAULT_ADHESION_COEFFICIENT = 1.0;
|
||||||
|
constexpr double DEFAULT_YIELD_STRENGTH = 0.02;
|
||||||
|
constexpr double DEFAULT_THERMAL_LENGTH = 200.0;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const std::vector<MaterialTypeInfo>& MaterialType::all()
|
||||||
|
{
|
||||||
|
static const std::vector<MaterialTypeInfo> material_types = {
|
||||||
|
{"ABS", 190, 300, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ABS-CF", 220, 300, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ABS-GF", 240, 280, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ASA", 220, 300, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ASA-CF", 230, 300, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ASA-GF", 240, 300, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"ASA-AERO", 240, 280, 50, 65, 1, 0.1 , 100},
|
||||||
|
{"BVOH", 190, 240, 0, 70, 1, 0.02, 200},
|
||||||
|
{"EVA", 175, 220, 0, 50, 1, 0.02, 200},
|
||||||
|
{"FLEX", 210, 230, 0, 50, 0.5, 0.02, 1000},
|
||||||
|
{"HIPS", 220, 270, 50, 60, 1, 0.02, 200},
|
||||||
|
{"PA", 235, 280, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA-CF", 240, 315, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA-GF", 240, 290, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA6", 260, 300, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA6-CF", 230, 300, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA6-GF", 260, 300, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA11", 275, 295, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA11-CF", 275, 295, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA11-GF", 275, 295, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA12", 250, 270, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA12-CF", 250, 300, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PA12-GF", 255, 270, 50, 60, 1, 0.02, 100},
|
||||||
|
{"PAHT", 260, 310, 55, 65, 1, 0.02, 200},
|
||||||
|
{"PAHT-CF", 270, 310, 55, 65, 1, 0.02, 200},
|
||||||
|
{"PAHT-GF", 270, 310, 55, 65, 1, 0.02, 200},
|
||||||
|
{"PC", 240, 300, 60, 70, 1, 0.02, 40},
|
||||||
|
{"PC-ABS", 230, 270, 60, 70, 1, 0.02, 80},
|
||||||
|
{"PC-CF", 270, 295, 60, 70, 1, 0.02, 80},
|
||||||
|
{"PC-PBT", 260, 300, 60, 70, 1, 0.02, 40},
|
||||||
|
{"PCL", 130, 170, 0, 45, 1, 0.02, 200},
|
||||||
|
{"PCTG", 220, 300, 0, 55, 2, 0.02, 200},
|
||||||
|
{"PE", 175, 260, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PE-CF", 175, 260, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PE-GF", 230, 270, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PEI-1010", 370, 430, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEI-1010-CF", 380, 430, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEI-1010-GF", 380, 430, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEI-9085", 350, 390, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEI-9085-CF", 365, 390, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEI-9085-GF", 370, 390, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEEK", 350, 460, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEEK-CF", 380, 410, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEEK-GF", 375, 410, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEKK", 325, 400, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PEKK-CF", 360, 400, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PES", 340, 390, 80, 100, 1, 0.02, 200},
|
||||||
|
{"PET", 200, 290, 0, 55, 2, 0.3 , 100},
|
||||||
|
{"PET-CF", 240, 320, 0, 55, 2, 0.3 , 100},
|
||||||
|
{"PET-GF", 280, 320, 0, 55, 2, 0.3 , 100},
|
||||||
|
{"PETG", 190, 260, 0, 55, 2, 0.3 , 100},
|
||||||
|
{"PETG-CF", 230, 290, 0, 55, 1, 0.3 , 100},
|
||||||
|
{"PETG-GF", 210, 270, 0, 55, 1, 0.3 , 100},
|
||||||
|
{"PHA", 190, 250, 0, 55, 1, 0.02, 200},
|
||||||
|
{"PI", 390, 410, 90, 100, 1, 0.02, 200},
|
||||||
|
{"PLA", 180, 240, 0, 45, 1, 0.02, 200},
|
||||||
|
{"PLA-AERO", 220, 270, 0, 55, 1, 0.02, 200},
|
||||||
|
{"PLA-CF", 190, 250, 0, 50, 1, 0.02, 200},
|
||||||
|
{"POM", 210, 250, 50, 65, 1, 0.02, 200},
|
||||||
|
{"PP", 200, 240, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PP-CF", 210, 250, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PP-GF", 220, 260, 45, 60, 1, 0.02, 200},
|
||||||
|
{"PPA-CF", 260, 300, 55, 70, 1, 0.02, 200},
|
||||||
|
{"PPA-GF", 260, 290, 55, 70, 1, 0.02, 200},
|
||||||
|
{"PPS", 300, 345, 90, 100, 1, 0.02, 200},
|
||||||
|
{"PPS-CF", 295, 350, 90, 100, 1, 0.02, 200},
|
||||||
|
{"PPSU", 360, 420, 90, 100, 1, 0.02, 200},
|
||||||
|
{"PSU", 350, 380, 90, 100, 1, 0.02, 200},
|
||||||
|
{"PVA", 185, 250, 0, 60, 1, 0.02, 200},
|
||||||
|
{"PVB", 190, 250, 0, 55, 1, 0.02, 200},
|
||||||
|
{"PVDF", 245, 265, 40, 60, 1, 0.02, 200},
|
||||||
|
{"SBS", 195, 250, 0, 55, 1, 0.02, 200},
|
||||||
|
{"TPI", 420, 445, 90, 100, 1, 0.02, 200},
|
||||||
|
{"TPU", 175, 260, 0, 50, 0.5, 0.02, 1000}
|
||||||
|
};
|
||||||
|
|
||||||
|
return material_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaterialTypeInfo* MaterialType::find(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto& types = all();
|
||||||
|
const auto it = std::find_if(types.begin(), types.end(), [&name](const MaterialTypeInfo& info) { return info.name == name; });
|
||||||
|
return it != types.end() ? &(*it) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialType::get_temperature_range(const std::string& type, int& min_temp, int& max_temp)
|
||||||
|
{
|
||||||
|
min_temp = DEFAULT_MIN_TEMP;
|
||||||
|
max_temp = DEFAULT_MAX_TEMP;
|
||||||
|
|
||||||
|
if (const auto* info = find(type)) {
|
||||||
|
min_temp = info->min_temp;
|
||||||
|
max_temp = info->max_temp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialType::get_chamber_temperature_range(const std::string& type, int& chamber_min_temp, int& chamber_max_temp)
|
||||||
|
{
|
||||||
|
chamber_min_temp = DEFAULT_CHAMBER_MIN_TEMP;
|
||||||
|
chamber_max_temp = DEFAULT_CHAMBER_MAX_TEMP;
|
||||||
|
|
||||||
|
if (const auto* info = find(type)) {
|
||||||
|
chamber_min_temp = info->chamber_min_temp;
|
||||||
|
chamber_max_temp = info->chamber_max_temp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialType::get_adhesion_coefficient(const std::string& type, double& adhesion_coefficient)
|
||||||
|
{
|
||||||
|
adhesion_coefficient = DEFAULT_ADHESION_COEFFICIENT;
|
||||||
|
|
||||||
|
if (const auto* info = find(type)) {
|
||||||
|
adhesion_coefficient = info->adhesion_coefficient;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialType::get_yield_strength(const std::string& type, double& yield_strength)
|
||||||
|
{
|
||||||
|
yield_strength = DEFAULT_YIELD_STRENGTH;
|
||||||
|
|
||||||
|
if (const auto* info = find(type)) {
|
||||||
|
yield_strength = info->yield_strength;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialType::get_thermal_length(const std::string& type, double& thermal_length)
|
||||||
|
{
|
||||||
|
thermal_length = DEFAULT_THERMAL_LENGTH;
|
||||||
|
|
||||||
|
if (const auto* info = find(type)) {
|
||||||
|
thermal_length = info->thermal_length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
32
src/libslic3r/MaterialType.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct MaterialTypeInfo {
|
||||||
|
std::string name;
|
||||||
|
int min_temp;
|
||||||
|
int max_temp;
|
||||||
|
int chamber_min_temp;
|
||||||
|
int chamber_max_temp;
|
||||||
|
double adhesion_coefficient;
|
||||||
|
double yield_strength;
|
||||||
|
double thermal_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MaterialType {
|
||||||
|
public:
|
||||||
|
static const std::vector<MaterialTypeInfo>& all();
|
||||||
|
|
||||||
|
static const MaterialTypeInfo* find(const std::string& name);
|
||||||
|
|
||||||
|
static bool get_temperature_range(const std::string& type, int& min_temp, int& max_temp);
|
||||||
|
static bool get_chamber_temperature_range(const std::string& type, int& chamber_min_temp, int& chamber_max_temp);
|
||||||
|
static bool get_adhesion_coefficient(const std::string& type, double& adhesion_coefficient);
|
||||||
|
static bool get_yield_strength(const std::string& type, double& yield_strength);
|
||||||
|
static bool get_thermal_length(const std::string& type, double& thermal_length);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "MTUtils.hpp"
|
#include "MTUtils.hpp"
|
||||||
#include "TriangleMeshSlicer.hpp"
|
#include "TriangleMeshSlicer.hpp"
|
||||||
#include "TriangleSelector.hpp"
|
#include "TriangleSelector.hpp"
|
||||||
|
#include "MaterialType.hpp"
|
||||||
|
|
||||||
#include "Format/AMF.hpp"
|
#include "Format/AMF.hpp"
|
||||||
#include "Format/svg.hpp"
|
#include "Format/svg.hpp"
|
||||||
@@ -3163,18 +3164,10 @@ double Model::getThermalLength(const ModelVolume* modelVolumePtr) {
|
|||||||
double thermalLength = 200.;
|
double thermalLength = 200.;
|
||||||
auto aa = modelVolumePtr->extruder_id();
|
auto aa = modelVolumePtr->extruder_id();
|
||||||
if (Model::extruderParamsMap.find(aa) != Model::extruderParamsMap.end()) {
|
if (Model::extruderParamsMap.find(aa) != Model::extruderParamsMap.end()) {
|
||||||
if (Model::extruderParamsMap.at(aa).materialName == "ABS" ||
|
double thermal_length = 200.0;
|
||||||
Model::extruderParamsMap.at(aa).materialName == "PA-CF" ||
|
if (MaterialType::get_thermal_length(Model::extruderParamsMap.at(aa).materialName, thermal_length)) {
|
||||||
Model::extruderParamsMap.at(aa).materialName == "PET-CF") {
|
return thermal_length;
|
||||||
thermalLength = 100;
|
|
||||||
}
|
}
|
||||||
if (Model::extruderParamsMap.at(aa).materialName == "PC") {
|
|
||||||
thermalLength = 40;
|
|
||||||
}
|
|
||||||
if (Model::extruderParamsMap.at(aa).materialName == "TPU") {
|
|
||||||
thermalLength = 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return thermalLength;
|
return thermalLength;
|
||||||
}
|
}
|
||||||
@@ -3237,16 +3230,10 @@ void ModelInstance::invalidate_convex_hull_2d()
|
|||||||
//BBS adhesion coefficients from model object class
|
//BBS adhesion coefficients from model object class
|
||||||
double getadhesionCoeff(const ModelVolumePtrs objectVolumes)
|
double getadhesionCoeff(const ModelVolumePtrs objectVolumes)
|
||||||
{
|
{
|
||||||
double adhesionCoeff = 1;
|
double adhesionCoeff = 1.0;
|
||||||
for (const ModelVolume* modelVolume : objectVolumes) {
|
for (const ModelVolume* modelVolume : objectVolumes) {
|
||||||
if (Model::extruderParamsMap.find(modelVolume->extruder_id()) != Model::extruderParamsMap.end()) {
|
if (Model::extruderParamsMap.find(modelVolume->extruder_id()) != Model::extruderParamsMap.end()) {
|
||||||
if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "PETG" ||
|
MaterialType::get_adhesion_coefficient(Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName, adhesionCoeff);
|
||||||
Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "PCTG") {
|
|
||||||
adhesionCoeff = 2;
|
|
||||||
}
|
|
||||||
else if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "TPU") {
|
|
||||||
adhesionCoeff = 0.5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return adhesionCoeff;
|
return adhesionCoeff;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "GCode/WipeTower2.hpp"
|
#include "GCode/WipeTower2.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
|
#include "MaterialType.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
@@ -57,6 +58,13 @@ PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(con
|
|||||||
// ORCA: Now this is a parameter
|
// ORCA: Now this is a parameter
|
||||||
//float Print::min_skirt_length = 0;
|
//float Print::min_skirt_length = 0;
|
||||||
|
|
||||||
|
struct FilamentType {
|
||||||
|
std::string name;
|
||||||
|
int min_temp;
|
||||||
|
int max_temp;
|
||||||
|
std::string temp_type;
|
||||||
|
};
|
||||||
|
|
||||||
void Print::clear()
|
void Print::clear()
|
||||||
{
|
{
|
||||||
std::scoped_lock<std::mutex> lock(this->state_mutex());
|
std::scoped_lock<std::mutex> lock(this->state_mutex());
|
||||||
@@ -2707,6 +2715,14 @@ Vec2d Print::translate_to_print_space(const Point &point) const {
|
|||||||
|
|
||||||
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
|
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
|
||||||
{
|
{
|
||||||
|
// FilamentTempType Temperature-based logic
|
||||||
|
int min_temp, max_temp;
|
||||||
|
if (MaterialType::get_temperature_range(filament_type, min_temp, max_temp)) {
|
||||||
|
if (max_temp <= 250) return FilamentTempType::LowTemp;
|
||||||
|
else if (max_temp < 280) return FilamentTempType::HighLowCompatible;
|
||||||
|
else return FilamentTempType::HighTemp;
|
||||||
|
}
|
||||||
|
|
||||||
const static std::string HighTempFilamentStr = "high_temp_filament";
|
const static std::string HighTempFilamentStr = "high_temp_filament";
|
||||||
const static std::string LowTempFilamentStr = "low_temp_filament";
|
const static std::string LowTempFilamentStr = "low_temp_filament";
|
||||||
const static std::string HighLowCompatibleFilamentStr = "high_low_compatible_filament";
|
const static std::string HighLowCompatibleFilamentStr = "high_low_compatible_filament";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
#include "MaterialType.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
|
|
||||||
@@ -54,6 +55,8 @@ namespace Slic3r {
|
|||||||
#define L(s) (s)
|
#define L(s) (s)
|
||||||
#define _(s) Slic3r::I18N::translate(s)
|
#define _(s) Slic3r::I18N::translate(s)
|
||||||
|
|
||||||
|
// Filament types are defined in MaterialType.
|
||||||
|
|
||||||
|
|
||||||
const std::vector<std::string> filament_extruder_override_keys = {
|
const std::vector<std::string> filament_extruder_override_keys = {
|
||||||
// floats
|
// floats
|
||||||
@@ -2503,46 +2506,11 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||||
def->gui_flags = "show_value";
|
def->gui_flags = "show_value";
|
||||||
|
|
||||||
def->enum_values.push_back("ABS");
|
// Populate the enum values using the shared material type database
|
||||||
def->enum_values.push_back("ABS-GF");
|
for (const auto& filament : MaterialType::all()) {
|
||||||
def->enum_values.push_back("ASA");
|
def->enum_values.push_back(filament.name);
|
||||||
def->enum_values.push_back("ASA-AERO");
|
}
|
||||||
def->enum_values.push_back("ASA-CF");
|
|
||||||
def->enum_values.push_back("BVOH");
|
|
||||||
def->enum_values.push_back("PCTG");
|
|
||||||
def->enum_values.push_back("EVA");
|
|
||||||
def->enum_values.push_back("FLEX");
|
|
||||||
def->enum_values.push_back("HIPS");
|
|
||||||
def->enum_values.push_back("PA");
|
|
||||||
def->enum_values.push_back("PA-CF");
|
|
||||||
def->enum_values.push_back("PA-GF");
|
|
||||||
def->enum_values.push_back("PA6-CF");
|
|
||||||
def->enum_values.push_back("PA11-CF");
|
|
||||||
def->enum_values.push_back("PC");
|
|
||||||
def->enum_values.push_back("PC-CF");
|
|
||||||
def->enum_values.push_back("PCTG");
|
|
||||||
def->enum_values.push_back("PE");
|
|
||||||
def->enum_values.push_back("PE-CF");
|
|
||||||
def->enum_values.push_back("PET-CF");
|
|
||||||
def->enum_values.push_back("PETG");
|
|
||||||
def->enum_values.push_back("PETG-CF");
|
|
||||||
def->enum_values.push_back("PETG-CF10");
|
|
||||||
def->enum_values.push_back("PETG-GF");
|
|
||||||
def->enum_values.push_back("PHA");
|
|
||||||
def->enum_values.push_back("PLA");
|
|
||||||
def->enum_values.push_back("PLA-AERO");
|
|
||||||
def->enum_values.push_back("PLA-CF");
|
|
||||||
def->enum_values.push_back("PP");
|
|
||||||
def->enum_values.push_back("PP-CF");
|
|
||||||
def->enum_values.push_back("PP-GF");
|
|
||||||
def->enum_values.push_back("PPA-CF");
|
|
||||||
def->enum_values.push_back("PPA-GF");
|
|
||||||
def->enum_values.push_back("PPS");
|
|
||||||
def->enum_values.push_back("PPS-CF");
|
|
||||||
def->enum_values.push_back("PVA");
|
|
||||||
def->enum_values.push_back("PVB");
|
|
||||||
def->enum_values.push_back("SBS");
|
|
||||||
def->enum_values.push_back("TPU");
|
|
||||||
def->mode = comSimple;
|
def->mode = comSimple;
|
||||||
def->set_default_value(new ConfigOptionStrings { "PLA" });
|
def->set_default_value(new ConfigOptionStrings { "PLA" });
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ namespace marchsq {
|
|||||||
// Specialize this struct to register a raster type for the Marching squares alg
|
// Specialize this struct to register a raster type for the Marching squares alg
|
||||||
template<> struct _RasterTraits<Slic3r::sla::RasterGrayscaleAA> {
|
template<> struct _RasterTraits<Slic3r::sla::RasterGrayscaleAA> {
|
||||||
using Rst = Slic3r::sla::RasterGrayscaleAA;
|
using Rst = Slic3r::sla::RasterGrayscaleAA;
|
||||||
|
|
||||||
// The type of pixel cell in the raster
|
// The type of pixel cell in the raster
|
||||||
using ValueType = uint8_t;
|
using ValueType = uint8_t;
|
||||||
|
|
||||||
// Value at a given position
|
// Value at a given position
|
||||||
static uint8_t get(const Rst &rst, size_t row, size_t col) { return rst.read_pixel(col, row); }
|
static uint8_t get(const Rst &rst, size_t row, size_t col) { return rst.read_pixel(col, row); }
|
||||||
|
|
||||||
// Number of rows and cols of the raster
|
// Number of rows and cols of the raster
|
||||||
static size_t rows(const Rst &rst) { return rst.resolution().height_px; }
|
static size_t rows(const Rst &rst) { return rst.resolution().height_px; }
|
||||||
static size_t cols(const Rst &rst) { return rst.resolution().width_px; }
|
static size_t cols(const Rst &rst) { return rst.resolution().width_px; }
|
||||||
@@ -34,57 +34,55 @@ template<class Fn> void foreach_vertex(ExPolygon &poly, Fn &&fn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i32 windowsize)
|
ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i32 windowsize)
|
||||||
{
|
{
|
||||||
size_t rows = rst.resolution().height_px, cols = rst.resolution().width_px;
|
size_t rows = rst.resolution().height_px, cols = rst.resolution().width_px;
|
||||||
|
|
||||||
if (rows < 2 || cols < 2) return {};
|
if (rows < 2 || cols < 2) return {};
|
||||||
|
|
||||||
Polygons polys;
|
Polygons polys;
|
||||||
long w_rows = std::max(2l, long(windowsize.y()));
|
long w_rows = std::max(1l, long(windowsize.y()));
|
||||||
long w_cols = std::max(2l, long(windowsize.x()));
|
long w_cols = std::max(1l, long(windowsize.x()));
|
||||||
|
|
||||||
std::vector<marchsq::Ring> rings =
|
std::vector<marchsq::Ring> rings =
|
||||||
marchsq::execute(rst, 128, {w_rows, w_cols});
|
marchsq::execute(rst, 128, {w_rows, w_cols});
|
||||||
|
|
||||||
polys.reserve(rings.size());
|
polys.reserve(rings.size());
|
||||||
|
|
||||||
auto pxd = rst.pixel_dimensions();
|
auto pxd = rst.pixel_dimensions();
|
||||||
pxd.w_mm = (rst.resolution().width_px * pxd.w_mm) / (rst.resolution().width_px - 1);
|
|
||||||
pxd.h_mm = (rst.resolution().height_px * pxd.h_mm) / (rst.resolution().height_px - 1);
|
|
||||||
|
|
||||||
for (const marchsq::Ring &ring : rings) {
|
for (const marchsq::Ring &ring : rings) {
|
||||||
Polygon poly; Points &pts = poly.points;
|
Polygon poly; Points &pts = poly.points;
|
||||||
pts.reserve(ring.size());
|
pts.reserve(ring.size());
|
||||||
|
|
||||||
for (const marchsq::Coord &crd : ring)
|
for (const marchsq::Coord &crd : ring)
|
||||||
pts.emplace_back(scaled(crd.c * pxd.w_mm), scaled(crd.r * pxd.h_mm));
|
pts.emplace_back(scaled(crd.c * pxd.w_mm), scaled(crd.r * pxd.h_mm));
|
||||||
|
|
||||||
polys.emplace_back(poly);
|
polys.emplace_back(poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reverse the raster transformations
|
// reverse the raster transformations
|
||||||
ExPolygons unioned = union_ex(polys);
|
ExPolygons unioned = union_ex(polys);
|
||||||
coord_t width = scaled(cols * pxd.h_mm), height = scaled(rows * pxd.w_mm);
|
coord_t width = scaled(cols * pxd.h_mm), height = scaled(rows * pxd.w_mm);
|
||||||
|
|
||||||
auto tr = rst.trafo();
|
auto tr = rst.trafo();
|
||||||
for (ExPolygon &expoly : unioned) {
|
for (ExPolygon &expoly : unioned) {
|
||||||
if (tr.mirror_y)
|
if (tr.mirror_y)
|
||||||
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
|
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
|
||||||
|
|
||||||
if (tr.mirror_x)
|
if (tr.mirror_x)
|
||||||
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
|
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
|
||||||
|
|
||||||
expoly.translate(-tr.center_x, -tr.center_y);
|
expoly.translate(-tr.center_x, -tr.center_y);
|
||||||
|
|
||||||
if (tr.flipXY)
|
if (tr.flipXY)
|
||||||
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
|
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
|
||||||
|
|
||||||
if ((tr.mirror_x + tr.mirror_y + tr.flipXY) % 2) {
|
if ((tr.mirror_x + tr.mirror_y + tr.flipXY) % 2) {
|
||||||
expoly.contour.reverse();
|
expoly.contour.reverse();
|
||||||
for (auto &h : expoly.holes) h.reverse();
|
for (auto &h : expoly.holes) h.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return unioned;
|
return unioned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace sla {
|
|||||||
|
|
||||||
class RasterGrayscaleAA;
|
class RasterGrayscaleAA;
|
||||||
|
|
||||||
ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i32 windowsize = {2, 2});
|
ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i32 windowsize = {1, 1});
|
||||||
|
|
||||||
}} // namespace Slic3r::sla
|
}} // namespace Slic3r::sla
|
||||||
|
|
||||||
|
|||||||
60
src/libslic3r/StreamUtils.hpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef slic3r_StreamUtils_hpp_
|
||||||
|
#define slic3r_StreamUtils_hpp_
|
||||||
|
|
||||||
|
#include "Point.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "Polygon.hpp"
|
||||||
|
#include "Polyline.hpp"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Points& pts)
|
||||||
|
{
|
||||||
|
os << "[" << pts.size() << "]:";
|
||||||
|
for (Point p : pts)
|
||||||
|
os << " (" << p << ")";
|
||||||
|
os << "\n";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const MultiPoint& mpts)
|
||||||
|
{
|
||||||
|
os << "Multipoint" << mpts.points;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Polygon& poly)
|
||||||
|
{
|
||||||
|
os << "Polygon" << poly.points;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Polygons& polys)
|
||||||
|
{
|
||||||
|
os << "Polygons[" << polys.size() << "]:" << "\n";
|
||||||
|
for (Polygon p : polys)
|
||||||
|
os << " " << p;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const ExPolygon& epoly)
|
||||||
|
{
|
||||||
|
os << "ExPolygon:\n";
|
||||||
|
os << " contour: " << epoly.contour;
|
||||||
|
os << " holes: " << epoly.holes;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const ExPolygons& epolys)
|
||||||
|
{
|
||||||
|
os << "ExPolygons[" << epolys.size() << "]:" << "\n";
|
||||||
|
for (ExPolygon p : epolys)
|
||||||
|
os << " " << p;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // slic3r_StreamUtils_hpp_
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
#include "PrintBase.hpp"
|
#include "PrintBase.hpp"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
|
#include "MaterialType.hpp"
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -64,15 +65,9 @@ struct Params
|
|||||||
return get_support_spots_adhesion_strength() * 2.0;
|
return get_support_spots_adhesion_strength() * 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filament_type == "PLA") {
|
double yield_strength = 0.02;
|
||||||
return 0.02 * 1e6;
|
MaterialType::get_yield_strength(filament_type, yield_strength);
|
||||||
} else if (filament_type == "PET" || filament_type == "PETG") {
|
return yield_strength * 1e6;
|
||||||
return 0.3 * 1e6;
|
|
||||||
} else if (filament_type == "ABS" || filament_type == "ASA") {
|
|
||||||
return 0.1 * 1e6; //TODO do measurements
|
|
||||||
} else { //PLA default value - defensive approach, PLA has quite low adhesion
|
|
||||||
return 0.02 * 1e6;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double get_support_spots_adhesion_strength() const {
|
double get_support_spots_adhesion_strength() const {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#include "libslic3r/MaterialType.hpp"
|
||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
@@ -60,10 +61,29 @@ void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrint
|
|||||||
int temperature_range_low, temperature_range_high;
|
int temperature_range_low, temperature_range_high;
|
||||||
if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return;
|
if (!get_temperature_range(config, temperature_range_low, temperature_range_high)) return;
|
||||||
|
|
||||||
|
// Get the selected filament type
|
||||||
|
std::string filament_type = "";
|
||||||
|
if (config->has("filament_type") && config->option<ConfigOptionStrings>("filament_type")->values.size() > 0) {
|
||||||
|
filament_type = config->option<ConfigOptionStrings>("filament_type")->values[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int min_recommended_temp = 190;
|
||||||
|
int max_recommended_temp = 300;
|
||||||
|
|
||||||
|
if (!MaterialType::get_temperature_range(filament_type, min_recommended_temp, max_recommended_temp)){
|
||||||
|
filament_type = "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
wxString msg_text;
|
wxString msg_text;
|
||||||
bool need_check = false;
|
bool need_check = false;
|
||||||
if (temperature_range_low < 190 || temperature_range_high > 300) {
|
if (temperature_range_low < min_recommended_temp) {
|
||||||
msg_text += _L("The recommended minimum temperature is less than 190°C or the recommended maximum temperature is greater than 300°C.\n");
|
msg_text += wxString::Format(_L("A minimum temperature above %d\u2103 is recommended for %s.\n"),
|
||||||
|
min_recommended_temp, filament_type);
|
||||||
|
need_check = true;
|
||||||
|
}
|
||||||
|
if (temperature_range_high > max_recommended_temp) {
|
||||||
|
msg_text += wxString::Format(_L("A maximum temperature below %d\u2103 is recommended for %s.\n"),
|
||||||
|
max_recommended_temp, filament_type);
|
||||||
need_check = true;
|
need_check = true;
|
||||||
}
|
}
|
||||||
if (temperature_range_low > temperature_range_high) {
|
if (temperature_range_low > temperature_range_high) {
|
||||||
@@ -146,23 +166,14 @@ void ConfigManipulation::check_filament_max_volumetric_speed(DynamicPrintConfig
|
|||||||
|
|
||||||
void ConfigManipulation::check_chamber_temperature(DynamicPrintConfig* config)
|
void ConfigManipulation::check_chamber_temperature(DynamicPrintConfig* config)
|
||||||
{
|
{
|
||||||
const static std::map<std::string, int>recommend_temp_map = {
|
|
||||||
{"PLA",45},
|
|
||||||
{"PLA-CF",45},
|
|
||||||
{"PVA",45},
|
|
||||||
{"TPU",50},
|
|
||||||
{"PETG",55},
|
|
||||||
{"PCTG",55},
|
|
||||||
{"PETG-CF",55}
|
|
||||||
};
|
|
||||||
bool support_chamber_temp_control=GUI::wxGetApp().preset_bundle->printers.get_selected_preset().config.opt_bool("support_chamber_temp_control");
|
bool support_chamber_temp_control=GUI::wxGetApp().preset_bundle->printers.get_selected_preset().config.opt_bool("support_chamber_temp_control");
|
||||||
if (support_chamber_temp_control&&config->has("chamber_temperature")) {
|
if (support_chamber_temp_control&&config->has("chamber_temperature")) {
|
||||||
std::string filament_type = config->option<ConfigOptionStrings>("filament_type")->get_at(0);
|
std::string filament_type = config->option<ConfigOptionStrings>("filament_type")->get_at(0);
|
||||||
auto iter = recommend_temp_map.find(filament_type);
|
int chamber_min_temp, chamber_max_temp;
|
||||||
if (iter!=recommend_temp_map.end()) {
|
if (MaterialType::get_chamber_temperature_range(filament_type, chamber_min_temp, chamber_max_temp)) {
|
||||||
if (iter->second < config->option<ConfigOptionInts>("chamber_temperature")->get_at(0)) {
|
if (chamber_max_temp < config->option<ConfigOptionInts>("chamber_temperature")->get_at(0)) {
|
||||||
wxString msg_text = wxString::Format(_L("Current chamber temperature is higher than the material's safe temperature, this may result in material softening and clogging. "
|
wxString msg_text = wxString::Format(_L("Current chamber temperature is higher than the material's safe temperature, this may result in material softening and clogging. "
|
||||||
"The maximum safe temperature for the material is %d"), iter->second);
|
"The maximum safe temperature for the material is %d"), chamber_max_temp);
|
||||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||||
is_msg_dlg_already_exist = true;
|
is_msg_dlg_already_exist = true;
|
||||||
dialog.ShowModal();
|
dialog.ShowModal();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ add_library(Catch2 INTERFACE)
|
|||||||
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2)
|
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2)
|
||||||
target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||||
|
target_compile_definitions(Catch2 INTERFACE -DCATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# OSX builds targeting OSX 10.9 do not support new std::uncought_exception()
|
# OSX builds targeting OSX 10.9 do not support new std::uncought_exception()
|
||||||
# see https://github.com/catchorg/Catch2/issues/1218
|
# see https://github.com/catchorg/Catch2/issues/1218
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
|
|
||||||
add_executable(${_TEST_NAME}_tests
|
add_executable(${_TEST_NAME}_tests
|
||||||
${_TEST_NAME}_tests.cpp
|
${_TEST_NAME}_tests.cpp
|
||||||
test_3mf.cpp
|
test_3mf.cpp
|
||||||
test_aabbindirect.cpp
|
test_aabbindirect.cpp
|
||||||
test_clipper_offset.cpp
|
test_clipper_offset.cpp
|
||||||
test_clipper_utils.cpp
|
test_clipper_utils.cpp
|
||||||
test_config.cpp
|
test_config.cpp
|
||||||
test_elephant_foot_compensation.cpp
|
test_elephant_foot_compensation.cpp
|
||||||
test_geometry.cpp
|
test_geometry.cpp
|
||||||
test_placeholder_parser.cpp
|
test_placeholder_parser.cpp
|
||||||
test_polygon.cpp
|
test_polygon.cpp
|
||||||
test_mutable_polygon.cpp
|
test_mutable_polygon.cpp
|
||||||
test_mutable_priority_queue.cpp
|
test_mutable_priority_queue.cpp
|
||||||
test_stl.cpp
|
test_stl.cpp
|
||||||
test_meshboolean.cpp
|
test_meshboolean.cpp
|
||||||
# test_marchingsquares.cpp
|
test_marchingsquares.cpp
|
||||||
test_timeutils.cpp
|
test_timeutils.cpp
|
||||||
test_voronoi.cpp
|
test_voronoi.cpp
|
||||||
test_optimizers.cpp
|
test_optimizers.cpp
|
||||||
# test_png_io.cpp
|
# test_png_io.cpp
|
||||||
test_indexed_triangle_set.cpp
|
test_indexed_triangle_set.cpp
|
||||||
../libnest2d/printer_parts.cpp
|
../libnest2d/printer_parts.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (TARGET OpenVDB::openvdb)
|
if (TARGET OpenVDB::openvdb)
|
||||||
target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp)
|
target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||||
orcaslicer_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
|
orcaslicer_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
|
||||||
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||||
orcaslicer_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
|
orcaslicer_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
|
||||||
else()
|
else()
|
||||||
orcaslicer_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
|
orcaslicer_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
|
|||||||
@@ -11,16 +11,16 @@
|
|||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
||||||
// CCW oriented contour
|
// CCW oriented contour
|
||||||
Slic3r::Polygon square{ { 200, 100 }, {200, 200}, {100, 200}, {100, 100} };
|
Slic3r::Polygon square{ { 200, 100 }, {200, 200}, {100, 200}, {100, 100} };
|
||||||
// CW oriented contour
|
// CW oriented contour
|
||||||
Slic3r::Polygon hole_in_square{ { 160, 140 }, { 140, 140 }, { 140, 160 }, { 160, 160 } };
|
Slic3r::Polygon hole_in_square{ { 160, 140 }, { 140, 140 }, { 140, 160 }, { 160, 160 } };
|
||||||
Slic3r::ExPolygon square_with_hole(square, hole_in_square);
|
Slic3r::ExPolygon square_with_hole(square, hole_in_square);
|
||||||
GIVEN("square_with_hole") {
|
GIVEN("square_with_hole") {
|
||||||
WHEN("offset") {
|
WHEN("offset") {
|
||||||
Polygons result = Slic3r::offset(square_with_hole, 5.f);
|
Polygons result = Slic3r::offset(square_with_hole, 5.f);
|
||||||
THEN("offset matches") {
|
THEN("offset matches") {
|
||||||
REQUIRE(result == Polygons {
|
REQUIRE(result == Polygons {
|
||||||
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
|
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
|
||||||
{ { 155, 145 }, { 145, 145 }, { 145, 155 }, { 155, 155 } } });
|
{ { 155, 145 }, { 145, 145 }, { 145, 155 }, { 155, 155 } } });
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
|||||||
WHEN("offset_ex") {
|
WHEN("offset_ex") {
|
||||||
ExPolygons result = Slic3r::offset_ex(square_with_hole, 5.f);
|
ExPolygons result = Slic3r::offset_ex(square_with_hole, 5.f);
|
||||||
THEN("offset matches") {
|
THEN("offset matches") {
|
||||||
REQUIRE(result == ExPolygons { {
|
REQUIRE(result == ExPolygons { {
|
||||||
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
|
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
|
||||||
{ { 145, 145 }, { 145, 155 }, { 155, 155 }, { 155, 145 } } } } );
|
{ { 145, 145 }, { 145, 155 }, { 155, 155 }, { 155, 145 } } } } );
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
|||||||
GIVEN("polyline") {
|
GIVEN("polyline") {
|
||||||
Polyline polyline { { 50, 150 }, { 300, 150 } };
|
Polyline polyline { { 50, 150 }, { 300, 150 } };
|
||||||
WHEN("intersection_pl") {
|
WHEN("intersection_pl") {
|
||||||
Polylines result = Slic3r::intersection_pl({ polyline }, { square, hole_in_square });
|
Polylines result = Slic3r::intersection_pl({ polyline }, Polygons{ square, hole_in_square });
|
||||||
THEN("correct number of result lines") {
|
THEN("correct number of result lines") {
|
||||||
REQUIRE(result.size() == 2);
|
REQUIRE(result.size() == 2);
|
||||||
}
|
}
|
||||||
@@ -93,82 +93,82 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GIVEN("Clipper bug #96 / Slic3r issue #2028") {
|
GIVEN("Clipper bug #96 / Slic3r issue #2028") {
|
||||||
Slic3r::Polyline subject{
|
Slic3r::Polyline subject{
|
||||||
{ 44735000, 31936670 }, { 55270000, 31936670 }, { 55270000, 25270000 }, { 74730000, 25270000 }, { 74730000, 44730000 }, { 68063296, 44730000 }, { 68063296, 55270000 }, { 74730000, 55270000 },
|
{ 44735000, 31936670 }, { 55270000, 31936670 }, { 55270000, 25270000 }, { 74730000, 25270000 }, { 74730000, 44730000 }, { 68063296, 44730000 }, { 68063296, 55270000 }, { 74730000, 55270000 },
|
||||||
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
|
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
|
||||||
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
|
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
|
||||||
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
|
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
|
||||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
Slic3r::Polylines result = Slic3r::intersection_pl(subject, Polygons{ clip });
|
||||||
THEN("intersection_pl - result is not empty") {
|
THEN("intersection_pl - result is not empty") {
|
||||||
REQUIRE(result.size() == 1);
|
REQUIRE(result.size() == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GIVEN("Clipper bug #122") {
|
GIVEN("Clipper bug #122") {
|
||||||
Slic3r::Polyline subject { { 1975, 1975 }, { 25, 1975 }, { 25, 25 }, { 1975, 25 }, { 1975, 1975 } };
|
Slic3r::Polyline subject { { 1975, 1975 }, { 25, 1975 }, { 25, 25 }, { 1975, 25 }, { 1975, 1975 } };
|
||||||
Slic3r::Polygons clip { { { 2025, 2025 }, { -25, 2025 } , { -25, -25 }, { 2025, -25 } },
|
Slic3r::Polygons clip { { { 2025, 2025 }, { -25, 2025 } , { -25, -25 }, { 2025, -25 } },
|
||||||
{ { 525, 525 }, { 525, 1475 }, { 1475, 1475 }, { 1475, 525 } } };
|
{ { 525, 525 }, { 525, 1475 }, { 1475, 1475 }, { 1475, 525 } } };
|
||||||
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, clip);
|
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, clip);
|
||||||
THEN("intersection_pl - result is not empty") {
|
THEN("intersection_pl - result is not empty") {
|
||||||
REQUIRE(result.size() == 1);
|
REQUIRE(result.size() == 1);
|
||||||
REQUIRE(result.front().points.size() == 5);
|
REQUIRE(result.front().points.size() == 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GIVEN("Clipper bug #126") {
|
GIVEN("Clipper bug #126") {
|
||||||
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
|
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
|
||||||
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
|
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
|
||||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
Slic3r::Polylines result = Slic3r::intersection_pl(subject, Polygons{ clip });
|
||||||
THEN("intersection_pl - result is not empty") {
|
THEN("intersection_pl - result is not empty") {
|
||||||
REQUIRE(result.size() == 1);
|
REQUIRE(result.size() == 1);
|
||||||
}
|
}
|
||||||
THEN("intersection_pl - result has same length as subject polyline") {
|
THEN("intersection_pl - result has same length as subject polyline") {
|
||||||
REQUIRE(result.front().length() == Approx(subject.length()));
|
REQUIRE(result.front().length() == Approx(subject.length()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
{
|
{
|
||||||
# Clipper does not preserve polyline orientation
|
# Clipper does not preserve polyline orientation
|
||||||
my $polyline = Slic3r::Polyline->new([50, 150], [300, 150]);
|
my $polyline = Slic3r::Polyline->new([50, 150], [300, 150]);
|
||||||
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
||||||
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
||||||
is_deeply $result->[0]->pp, [[100, 150], [200, 150]], 'clipped line orientation is preserved';
|
is_deeply $result->[0]->pp, [[100, 150], [200, 150]], 'clipped line orientation is preserved';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# Clipper does not preserve polyline orientation
|
# Clipper does not preserve polyline orientation
|
||||||
my $polyline = Slic3r::Polyline->new([300, 150], [50, 150]);
|
my $polyline = Slic3r::Polyline->new([300, 150], [50, 150]);
|
||||||
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
|
||||||
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
|
||||||
is_deeply $result->[0]->pp, [[200, 150], [100, 150]], 'clipped line orientation is preserved';
|
is_deeply $result->[0]->pp, [[200, 150], [100, 150]], 'clipped line orientation is preserved';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# Disabled until Clipper bug #127 is fixed
|
# Disabled until Clipper bug #127 is fixed
|
||||||
my $subject = [
|
my $subject = [
|
||||||
Slic3r::Polyline->new([-90000000, -100000000], [-90000000, 100000000]), # vertical
|
Slic3r::Polyline->new([-90000000, -100000000], [-90000000, 100000000]), # vertical
|
||||||
Slic3r::Polyline->new([-100000000, -10000000], [100000000, -10000000]), # horizontal
|
Slic3r::Polyline->new([-100000000, -10000000], [100000000, -10000000]), # horizontal
|
||||||
Slic3r::Polyline->new([-100000000, 0], [100000000, 0]), # horizontal
|
Slic3r::Polyline->new([-100000000, 0], [100000000, 0]), # horizontal
|
||||||
Slic3r::Polyline->new([-100000000, 10000000], [100000000, 10000000]), # horizontal
|
Slic3r::Polyline->new([-100000000, 10000000], [100000000, 10000000]), # horizontal
|
||||||
];
|
];
|
||||||
my $clip = Slic3r::Polygon->new(# a circular, convex, polygon
|
my $clip = Slic3r::Polygon->new(# a circular, convex, polygon
|
||||||
[99452190, 10452846], [97814760, 20791169], [95105652, 30901699], [91354546, 40673664], [86602540, 50000000],
|
[99452190, 10452846], [97814760, 20791169], [95105652, 30901699], [91354546, 40673664], [86602540, 50000000],
|
||||||
[80901699, 58778525], [74314483, 66913061], [66913061, 74314483], [58778525, 80901699], [50000000, 86602540],
|
[80901699, 58778525], [74314483, 66913061], [66913061, 74314483], [58778525, 80901699], [50000000, 86602540],
|
||||||
[40673664, 91354546], [30901699, 95105652], [20791169, 97814760], [10452846, 99452190], [0, 100000000],
|
[40673664, 91354546], [30901699, 95105652], [20791169, 97814760], [10452846, 99452190], [0, 100000000],
|
||||||
[-10452846, 99452190], [-20791169, 97814760], [-30901699, 95105652], [-40673664, 91354546],
|
[-10452846, 99452190], [-20791169, 97814760], [-30901699, 95105652], [-40673664, 91354546],
|
||||||
[-50000000, 86602540], [-58778525, 80901699], [-66913061, 74314483], [-74314483, 66913061],
|
[-50000000, 86602540], [-58778525, 80901699], [-66913061, 74314483], [-74314483, 66913061],
|
||||||
[-80901699, 58778525], [-86602540, 50000000], [-91354546, 40673664], [-95105652, 30901699],
|
[-80901699, 58778525], [-86602540, 50000000], [-91354546, 40673664], [-95105652, 30901699],
|
||||||
[-97814760, 20791169], [-99452190, 10452846], [-100000000, 0], [-99452190, -10452846],
|
[-97814760, 20791169], [-99452190, 10452846], [-100000000, 0], [-99452190, -10452846],
|
||||||
[-97814760, -20791169], [-95105652, -30901699], [-91354546, -40673664], [-86602540, -50000000],
|
[-97814760, -20791169], [-95105652, -30901699], [-91354546, -40673664], [-86602540, -50000000],
|
||||||
[-80901699, -58778525], [-74314483, -66913061], [-66913061, -74314483], [-58778525, -80901699],
|
[-80901699, -58778525], [-74314483, -66913061], [-66913061, -74314483], [-58778525, -80901699],
|
||||||
[-50000000, -86602540], [-40673664, -91354546], [-30901699, -95105652], [-20791169, -97814760],
|
[-50000000, -86602540], [-40673664, -91354546], [-30901699, -95105652], [-20791169, -97814760],
|
||||||
[-10452846, -99452190], [0, -100000000], [10452846, -99452190], [20791169, -97814760],
|
[-10452846, -99452190], [0, -100000000], [10452846, -99452190], [20791169, -97814760],
|
||||||
[30901699, -95105652], [40673664, -91354546], [50000000, -86602540], [58778525, -80901699],
|
[30901699, -95105652], [40673664, -91354546], [50000000, -86602540], [58778525, -80901699],
|
||||||
[66913061, -74314483], [74314483, -66913061], [80901699, -58778525], [86602540, -50000000],
|
[66913061, -74314483], [74314483, -66913061], [80901699, -58778525], [86602540, -50000000],
|
||||||
[91354546, -40673664], [95105652, -30901699], [97814760, -20791169], [99452190, -10452846], [100000000, 0]
|
[91354546, -40673664], [95105652, -30901699], [97814760, -20791169], [99452190, -10452846], [100000000, 0]
|
||||||
);
|
);
|
||||||
my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, [$clip]);
|
my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, [$clip]);
|
||||||
is scalar(@$result), scalar(@$subject), 'intersection_pl - expected number of polylines';
|
is scalar(@$result), scalar(@$subject), 'intersection_pl - expected number of polylines';
|
||||||
is sum(map scalar(@$_), @$result), scalar(@$subject) * 2, 'intersection_pl - expected number of points in polylines';
|
is sum(map scalar(@$_), @$result), scalar(@$subject) * 2, 'intersection_pl - expected number of points in polylines';
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("diff_ex with another square") {
|
WHEN("diff_ex with another square") {
|
||||||
ExPolygons diff = Slic3r::diff_ex(Polygons{ square, square2 }, Polygons{ hole });
|
ExPolygons diff = Slic3r::diff_ex(Polygons{ square, square2 }, Polygons{ hole });
|
||||||
THEN("difference of a cw from two ccw is a contour with one hole") {
|
THEN("difference of a cw from two ccw is a contour with one hole") {
|
||||||
REQUIRE(diff.size() == 1);
|
REQUIRE(diff.size() == 1);
|
||||||
REQUIRE(diff.front().area() == Approx(ExPolygon({ {40, 40}, {0, 40}, {0, 0}, {40, 0} }, { {15, 25}, {25, 25}, {25, 15}, {15, 15} }).area()));
|
REQUIRE(diff.front().area() == Approx(ExPolygon({ {40, 40}, {0, 40}, {0, 0}, {40, 0} }, { {15, 25}, {25, 25}, {25, 15}, {15, 15} }).area()));
|
||||||
@@ -225,11 +225,11 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<e_ordering o = e_ordering::OFF, class P, class Tree, class Alloc>
|
template<e_ordering o = e_ordering::OFF, class P, class Tree, class Alloc>
|
||||||
double polytree_area(const Tree &tree, std::vector<P, Alloc> *out)
|
double polytree_area(const Tree &tree, std::vector<P, Alloc> *out)
|
||||||
{
|
{
|
||||||
traverse_pt<o>(tree, out);
|
traverse_pt<o>(tree, out);
|
||||||
|
|
||||||
return std::accumulate(out->begin(), out->end(), 0.0,
|
return std::accumulate(out->begin(), out->end(), 0.0,
|
||||||
[](double a, const P &p) { return a + p.area(); });
|
[](double a, const P &p) { return a + p.area(); });
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ size_t count_polys(const ExPolygons& expolys)
|
|||||||
{
|
{
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
for (auto &ep : expolys) c += ep.holes.size() + 1;
|
for (auto &ep : expolys) c += ep.holes.size() + 1;
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,32 +247,32 @@ TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") {
|
|||||||
Polygon unitbox;
|
Polygon unitbox;
|
||||||
const auto UNIT = coord_t(1. / SCALING_FACTOR);
|
const auto UNIT = coord_t(1. / SCALING_FACTOR);
|
||||||
unitbox.points = { Vec2crd{0, 0}, Vec2crd{UNIT, 0}, Vec2crd{UNIT, UNIT}, Vec2crd{0, UNIT}};
|
unitbox.points = { Vec2crd{0, 0}, Vec2crd{UNIT, 0}, Vec2crd{UNIT, UNIT}, Vec2crd{0, UNIT}};
|
||||||
|
|
||||||
Polygon box_frame = unitbox;
|
Polygon box_frame = unitbox;
|
||||||
box_frame.scale(20, 10);
|
box_frame.scale(20, 10);
|
||||||
|
|
||||||
Polygon hole_left = unitbox;
|
Polygon hole_left = unitbox;
|
||||||
hole_left.scale(8);
|
hole_left.scale(8);
|
||||||
hole_left.translate(UNIT, UNIT);
|
hole_left.translate(UNIT, UNIT);
|
||||||
hole_left.reverse();
|
hole_left.reverse();
|
||||||
|
|
||||||
Polygon hole_right = hole_left;
|
Polygon hole_right = hole_left;
|
||||||
hole_right.translate(UNIT * 10, 0);
|
hole_right.translate(UNIT * 10, 0);
|
||||||
|
|
||||||
Polygon inner_left = unitbox;
|
Polygon inner_left = unitbox;
|
||||||
inner_left.scale(4);
|
inner_left.scale(4);
|
||||||
inner_left.translate(UNIT * 3, UNIT * 3);
|
inner_left.translate(UNIT * 3, UNIT * 3);
|
||||||
|
|
||||||
Polygon inner_right = inner_left;
|
Polygon inner_right = inner_left;
|
||||||
inner_right.translate(UNIT * 10, 0);
|
inner_right.translate(UNIT * 10, 0);
|
||||||
|
|
||||||
Polygons reference = union_({box_frame, hole_left, hole_right, inner_left, inner_right});
|
Polygons reference = union_({box_frame, hole_left, hole_right, inner_left, inner_right});
|
||||||
|
|
||||||
ClipperLib::PolyTree tree = union_pt(reference);
|
ClipperLib::PolyTree tree = union_pt(reference);
|
||||||
double area_sum = box_frame.area() + hole_left.area() +
|
double area_sum = box_frame.area() + hole_left.area() +
|
||||||
hole_right.area() + inner_left.area() +
|
hole_right.area() + inner_left.area() +
|
||||||
inner_right.area();
|
inner_right.area();
|
||||||
|
|
||||||
REQUIRE(area_sum > 0);
|
REQUIRE(area_sum > 0);
|
||||||
|
|
||||||
SECTION("Traverse into Polygons WITHOUT spatial ordering") {
|
SECTION("Traverse into Polygons WITHOUT spatial ordering") {
|
||||||
@@ -280,19 +280,19 @@ TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") {
|
|||||||
REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output)));
|
REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output)));
|
||||||
REQUIRE(output.size() == reference.size());
|
REQUIRE(output.size() == reference.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Traverse into ExPolygons WITHOUT spatial ordering") {
|
SECTION("Traverse into ExPolygons WITHOUT spatial ordering") {
|
||||||
ExPolygons output;
|
ExPolygons output;
|
||||||
REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output)));
|
REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output)));
|
||||||
REQUIRE(count_polys(output) == reference.size());
|
REQUIRE(count_polys(output) == reference.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Traverse into Polygons WITH spatial ordering") {
|
SECTION("Traverse into Polygons WITH spatial ordering") {
|
||||||
Polygons output;
|
Polygons output;
|
||||||
REQUIRE(area_sum == Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
|
REQUIRE(area_sum == Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
|
||||||
REQUIRE(output.size() == reference.size());
|
REQUIRE(output.size() == reference.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Traverse into ExPolygons WITH spatial ordering") {
|
SECTION("Traverse into ExPolygons WITH spatial ordering") {
|
||||||
ExPolygons output;
|
ExPolygons output;
|
||||||
REQUIRE(area_sum == Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
|
REQUIRE(area_sum == Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
|
||||||
|
|||||||
@@ -17,22 +17,111 @@
|
|||||||
#include <libslic3r/TriangulateWall.hpp>
|
#include <libslic3r/TriangulateWall.hpp>
|
||||||
#include <libslic3r/Tesselate.hpp>
|
#include <libslic3r/Tesselate.hpp>
|
||||||
#include <libslic3r/SlicesToTriangleMesh.hpp>
|
#include <libslic3r/SlicesToTriangleMesh.hpp>
|
||||||
|
#include <libslic3r/StreamUtils.hpp>
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
using namespace Catch::Matchers;
|
||||||
|
|
||||||
static double area(const sla::RasterBase::PixelDim &pxd)
|
// Note this tests SLA/RasterToPolygons.hpp, SLA/AGGRaster.hpp, and
|
||||||
|
// ClipperUtils.hpp at least as much as MarchingSquares.hpp.
|
||||||
|
|
||||||
|
// Get the Point corresponding to a raster column and row.
|
||||||
|
Point rstPoint(const sla::RasterGrayscaleAA& rst, const size_t c, const size_t r)
|
||||||
{
|
{
|
||||||
return pxd.w_mm * pxd.h_mm;
|
size_t rows = rst.resolution().height_px, cols = rst.resolution().width_px;
|
||||||
|
auto pxd = rst.pixel_dimensions();
|
||||||
|
auto tr = rst.trafo();
|
||||||
|
coord_t width = scaled(cols * pxd.h_mm), height = scaled(rows * pxd.w_mm);
|
||||||
|
Point p = Point::new_scale(c * pxd.w_mm, r * pxd.h_mm);
|
||||||
|
// reverse the raster transformations
|
||||||
|
if (tr.mirror_y)
|
||||||
|
p.y() = height - p.y();
|
||||||
|
if (tr.mirror_x)
|
||||||
|
p.x() = width - p.x();
|
||||||
|
p.x() -= tr.center_x;
|
||||||
|
p.y() -= tr.center_y;
|
||||||
|
if (tr.flipXY)
|
||||||
|
std::swap(p.x(), p.y());
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Slic3r::sla::RasterGrayscaleAA create_raster(
|
// Get the size of a raster pixel in coord_t.
|
||||||
const sla::RasterBase::Resolution &res,
|
static Point rstPixel(const sla::RasterGrayscaleAA& rst)
|
||||||
double disp_w = 100.,
|
|
||||||
double disp_h = 100.)
|
|
||||||
{
|
{
|
||||||
sla::RasterBase::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
|
auto pxd = rst.pixel_dimensions();
|
||||||
|
return Point::new_scale(pxd.w_mm, pxd.h_mm);
|
||||||
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
|
}
|
||||||
|
|
||||||
|
// Get the size of a raster in coord_t.
|
||||||
|
static Point rstSize(const sla::RasterGrayscaleAA& rst)
|
||||||
|
{
|
||||||
|
auto pxd = rst.pixel_dimensions();
|
||||||
|
auto res = rst.resolution();
|
||||||
|
return Point::new_scale(pxd.w_mm * res.width_px, pxd.h_mm * res.height_px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the bounding box of a raster.
|
||||||
|
static BoundingBox rstBBox(const sla::RasterGrayscaleAA& rst)
|
||||||
|
{
|
||||||
|
auto center = rst.trafo().get_center();
|
||||||
|
return BoundingBox(Point(0, 0) - center, rstSize(rst) - center);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ExPolygons directly corresponding to a raster.
|
||||||
|
static ExPolygons rstGetPolys(sla::RasterGrayscaleAA& rst)
|
||||||
|
{
|
||||||
|
size_t rows = rst.resolution().height_px, cols = rst.resolution().width_px;
|
||||||
|
Polygons polys;
|
||||||
|
for (auto r = 0; r < rows; r++) {
|
||||||
|
// use c0==cols as a sentinel marker for "no start column yet".
|
||||||
|
size_t c0 = cols;
|
||||||
|
for (auto c = 0; c <= cols; c++) {
|
||||||
|
if (c < cols && rst.read_pixel(c, r) > 128) {
|
||||||
|
// We have set pixels, set the c0 start column if it is not yet set.
|
||||||
|
if (c0 == cols)
|
||||||
|
c0 = c;
|
||||||
|
} else if (c0 < cols) {
|
||||||
|
// There is no pixel set, but we do have a c0 start column. Output a
|
||||||
|
// "row-rectangle" poly for this row between the start column c0 and
|
||||||
|
// the current column.
|
||||||
|
polys.push_back({rstPoint(rst, c0, r), rstPoint(rst, c0, r + 1), rstPoint(rst, c, r + 1), rstPoint(rst, c, r)});
|
||||||
|
// Make sure the poly is anti-clockwise, which it might not be
|
||||||
|
// depending on how rstPoint() reverses the raster transformations
|
||||||
|
// from (c,r) to (x,y) coordinates.
|
||||||
|
if (polys.back().is_clockwise())
|
||||||
|
polys.back().reverse();
|
||||||
|
// Clear the start column c0 for the next row-rectangle.
|
||||||
|
c0 = cols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Merge all the row-rectangle polys into contiguous raster ExPolygons.
|
||||||
|
return union_ex(polys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the length in mm of a "vector" Point.
|
||||||
|
static double len(const Point& v) { return unscaled(v.norm()); }
|
||||||
|
// Get the area in mm^2 of a box with corners at the origin and a Point.
|
||||||
|
static double area(const Point& v) { return unscaled(v.x()) * unscaled(v.y()); }
|
||||||
|
|
||||||
|
// Find the index of the nearest extracted ExPolygon for a reference ExPolygon.
|
||||||
|
static int find_closest_ext(const ExPolygons& exts, ExPolygon ref)
|
||||||
|
{
|
||||||
|
auto ref_center = ref.contour.bounding_box().center();
|
||||||
|
|
||||||
|
auto closest = std::min_element(exts.begin(), exts.end(), [&ref_center](auto a, auto b) {
|
||||||
|
auto a_center = a.contour.bounding_box().center();
|
||||||
|
auto b_center = b.contour.bounding_box().center();
|
||||||
|
return a_center.distance_to(ref_center) < b_center.distance_to(ref_center);
|
||||||
|
});
|
||||||
|
return std::distance(exts.begin(), closest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Slic3r::sla::RasterGrayscaleAA create_raster(const sla::Resolution& res, double disp_w = 100., double disp_h = 100.)
|
||||||
|
{
|
||||||
|
sla::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
|
||||||
|
|
||||||
|
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
|
||||||
sla::RasterBase::Trafo trafo;
|
sla::RasterBase::Trafo trafo;
|
||||||
trafo.center_x = bb.center().x();
|
trafo.center_x = bb.center().x();
|
||||||
trafo.center_y = bb.center().y();
|
trafo.center_y = bb.center().y();
|
||||||
@@ -43,334 +132,439 @@ static Slic3r::sla::RasterGrayscaleAA create_raster(
|
|||||||
static ExPolygon square(double a, Point center = {0, 0})
|
static ExPolygon square(double a, Point center = {0, 0})
|
||||||
{
|
{
|
||||||
ExPolygon poly;
|
ExPolygon poly;
|
||||||
coord_t V = scaled(a / 2.);
|
coord_t V = scaled(a / 2.);
|
||||||
|
|
||||||
poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}};
|
poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}};
|
||||||
poly.translate(center.x(), center.y());
|
poly.translate(center.x(), center.y());
|
||||||
|
|
||||||
return poly;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExPolygon square_with_hole(double a, Point center = {0, 0})
|
static ExPolygon square_with_hole(double a, Point center = {0, 0})
|
||||||
{
|
{
|
||||||
ExPolygon poly = square(a);
|
ExPolygon poly = square(a);
|
||||||
|
|
||||||
poly.holes.emplace_back();
|
poly.holes.emplace_back();
|
||||||
coord_t V = scaled(a / 4.);
|
coord_t V = scaled(a / 4.);
|
||||||
poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}};
|
poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}};
|
||||||
|
|
||||||
poly.translate(center.x(), center.y());
|
poly.translate(center.x(), center.y());
|
||||||
|
|
||||||
return poly;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExPolygons circle_with_hole(double r, Point center = {0, 0}) {
|
static ExPolygons circle_with_hole(double r, Point center = {0, 0})
|
||||||
|
{
|
||||||
ExPolygon poly;
|
ExPolygon poly;
|
||||||
|
|
||||||
std::vector<double> pis = linspace_vector(0., 2 * PI, 100);
|
std::vector<double> pis = linspace_vector(0., 2 * PI, 100);
|
||||||
|
|
||||||
coord_t rs = scaled(r);
|
coord_t rs = scaled(r);
|
||||||
for (double phi : pis) {
|
for (double phi : pis) {
|
||||||
poly.contour.points.emplace_back(rs * std::cos(phi), rs * std::sin(phi));
|
poly.contour.points.emplace_back(rs * std::cos(phi), rs * std::sin(phi));
|
||||||
}
|
}
|
||||||
|
|
||||||
poly.holes.emplace_back(poly.contour);
|
poly.holes.emplace_back(poly.contour);
|
||||||
poly.holes.front().reverse();
|
poly.holes.front().reverse();
|
||||||
for (auto &p : poly.holes.front().points) p /= 2;
|
for (auto& p : poly.holes.front().points)
|
||||||
|
p /= 2;
|
||||||
|
|
||||||
poly.translate(center.x(), center.y());
|
poly.translate(center.x(), center.y());
|
||||||
|
|
||||||
return {poly};
|
return {poly};
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Vec2i32 W4x4 = {4, 4};
|
|
||||||
static const Vec2i32 W2x2 = {2, 2};
|
static const Vec2i32 W2x2 = {2, 2};
|
||||||
|
static const Vec2i32 W1x1 = {1, 1};
|
||||||
|
|
||||||
template<class Rst>
|
template<class Rst>
|
||||||
static void test_expolys(Rst && rst,
|
static void test_expolys(Rst&& rst, const ExPolygons& ref, Vec2i32 window, const std::string& name = "test", bool strict = true)
|
||||||
const ExPolygons & ref,
|
|
||||||
Vec2i32 window,
|
|
||||||
const std::string &name = "test")
|
|
||||||
{
|
{
|
||||||
for (const ExPolygon &expoly : ref) rst.draw(expoly);
|
auto raster_bb = rstBBox(rst);
|
||||||
|
Point pixel_size = rstPixel(rst);
|
||||||
|
Point window_size{coord_t(pixel_size.x() * window.x()), coord_t(pixel_size.y() * window.y())};
|
||||||
|
double pixel_area = area(pixel_size);
|
||||||
|
double pixel_len = len(pixel_size);
|
||||||
|
double window_area = area(window_size);
|
||||||
|
double window_len = len(window_size);
|
||||||
|
|
||||||
|
for (const ExPolygon& expoly : ref)
|
||||||
|
rst.draw(expoly);
|
||||||
|
|
||||||
std::fstream out(name + ".png", std::ios::out);
|
std::fstream out(name + ".png", std::ios::out);
|
||||||
out << rst.encode(sla::PNGRasterEncoder{});
|
out << rst.encode(sla::PNGRasterEncoder{});
|
||||||
out.close();
|
out.close();
|
||||||
|
|
||||||
ExPolygons extracted = sla::raster_to_polygons(rst, window);
|
const ExPolygons bmp = rstGetPolys(rst);
|
||||||
|
const ExPolygons ext = sla::raster_to_polygons(rst, window);
|
||||||
SVG svg(name + ".svg");
|
|
||||||
svg.draw(extracted);
|
SVG svg(name + ".svg", raster_bb);
|
||||||
svg.draw(ref, "green");
|
svg.draw(bmp, "green");
|
||||||
|
if (pixel_size.x() >= scale_(0.5))
|
||||||
|
svg.draw_grid(raster_bb, "grey", scale_(0.05), pixel_size.x());
|
||||||
|
if (window_size.x() >= scale_(1.0))
|
||||||
|
svg.draw_grid(raster_bb, "grey", scale_(0.10), window_size.x());
|
||||||
|
svg.draw_outline(ref, "red", "red", scale_(0.3));
|
||||||
|
svg.draw_outline(ext, "blue", "blue");
|
||||||
svg.Close();
|
svg.Close();
|
||||||
|
|
||||||
double max_rel_err = 0.1;
|
// Note all these areas are unscaled back to mm^2.
|
||||||
sla::RasterBase::PixelDim pxd = rst.pixel_dimensions();
|
double raster_area = unscaled(unscaled(area(bmp)));
|
||||||
double max_abs_err = area(pxd) * scaled(1.) * scaled(1.);
|
double reference_area = unscaled(unscaled(area(ref)));
|
||||||
|
double extracted_area = unscaled(unscaled(area(ext)));
|
||||||
BoundingBox ref_bb;
|
|
||||||
for (auto &expoly : ref) ref_bb.merge(expoly.contour.bounding_box());
|
// Note that errors accumulate with each step going from the reference
|
||||||
|
// polys to the extracted polys. The rendering of the reference polys to
|
||||||
double max_displacement = 4. * (std::pow(pxd.h_mm, 2) + std::pow(pxd.w_mm, 2));
|
// the raster does introduce pixelization errors too. This checks for
|
||||||
max_displacement *= scaled<double>(1.) * scaled(1.);
|
// acceptable errors going from reference to raster, and raster to
|
||||||
|
// reference.
|
||||||
REQUIRE(extracted.size() == ref.size());
|
|
||||||
for (size_t i = 0; i < ref.size(); ++i) {
|
for (size_t i = 0; i < ref.size(); ++i) {
|
||||||
REQUIRE(extracted[i].contour.is_counter_clockwise());
|
if (ref[i].contour.size() < 20)
|
||||||
REQUIRE(extracted[i].holes.size() == ref[i].holes.size());
|
UNSCOPED_INFO("reference ref[" << i << "]: " << ref[i]);
|
||||||
|
}
|
||||||
for (auto &h : extracted[i].holes) REQUIRE(h.is_clockwise());
|
CHECK_THAT(raster_area, WithinRel(reference_area, pixel_len * 0.05) || WithinAbs(reference_area, pixel_area));
|
||||||
|
for (size_t i = 0; i < ext.size(); ++i) {
|
||||||
double refa = ref[i].area();
|
if (ext[i].contour.size() < 20)
|
||||||
double abs_err = std::abs(extracted[i].area() - refa);
|
UNSCOPED_INFO("extracted ext[" << i << "]: " << ext[i]);
|
||||||
double rel_err = abs_err / refa;
|
}
|
||||||
|
CHECK_THAT(extracted_area, WithinRel(raster_area, 0.05) || WithinAbs(raster_area, window_area));
|
||||||
REQUIRE((rel_err <= max_rel_err || abs_err <= max_abs_err));
|
for (auto i = 0; i < ext.size(); ++i) {
|
||||||
|
CHECK(ext[i].contour.is_counter_clockwise());
|
||||||
BoundingBox bb;
|
for (auto& h : ext[i].holes)
|
||||||
for (auto &expoly : extracted) bb.merge(expoly.contour.bounding_box());
|
CHECK(h.is_clockwise());
|
||||||
|
}
|
||||||
Point d = bb.center() - ref_bb.center();
|
|
||||||
REQUIRE(double(d.transpose() * d) <= max_displacement);
|
BoundingBox ref_bb;
|
||||||
|
for (auto& expoly : ref)
|
||||||
|
ref_bb.merge(expoly.contour.bounding_box());
|
||||||
|
BoundingBox ext_bb;
|
||||||
|
for (auto& expoly : ext)
|
||||||
|
ext_bb.merge(expoly.contour.bounding_box());
|
||||||
|
CHECK(len(ext_bb.center() - ref_bb.center()) < pixel_len);
|
||||||
|
|
||||||
|
// In ambigous cases (when polygons just touch) there are multiple equally
|
||||||
|
// valid interpretations of the raster into polygons. Although
|
||||||
|
// MarchingSquares currently systematically selects the solution that
|
||||||
|
// breaks them into separate polygons, that might not always be true. Also,
|
||||||
|
// SLA/RasterToPolygons.hpp, and in particular union_ex() from
|
||||||
|
// ClipperUtils.hpp that it uses, can and does sometimes merge them back
|
||||||
|
// together. This means we cannot reliably make assertions about the
|
||||||
|
// extracted number of polygons and their shapes in these cases. So we skip
|
||||||
|
// the individual polygon checks for strict=false.
|
||||||
|
if (strict) {
|
||||||
|
CHECK(ext.size() == ref.size());
|
||||||
|
for (auto i = 0; i < ext.size(); ++i) {
|
||||||
|
auto j = find_closest_ext(ref, ext[i]);
|
||||||
|
INFO("Comparing ext[" << i << "] against closest ref[" << j << "]");
|
||||||
|
CHECK(ext[i].holes.size() == ref[j].holes.size());
|
||||||
|
double ext_i_area = unscaled(unscaled(ext[i].area()));
|
||||||
|
double ref_j_area = unscaled(unscaled(ref[j].area()));
|
||||||
|
CHECK_THAT(ext_i_area, WithinRel(ref_j_area, pixel_len * 0.05) || WithinAbs(ref_j_area, window_area));
|
||||||
|
auto ext_i_bb = ext[i].contour.bounding_box();
|
||||||
|
auto ref_j_bb = ref[j].contour.bounding_box();
|
||||||
|
CHECK(len(ext_i_bb.center() - ref_j_bb.center()) < pixel_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Empty raster should result in empty polygons", "[MarchingSquares]") {
|
TEST_CASE("Empty raster should result in empty polygons", "[MarchingSquares]")
|
||||||
|
{
|
||||||
sla::RasterGrayscaleAAGammaPower rst{{}, {}, {}};
|
sla::RasterGrayscaleAAGammaPower rst{{}, {}, {}};
|
||||||
ExPolygons extracted = sla::raster_to_polygons(rst);
|
ExPolygons extracted = sla::raster_to_polygons(rst);
|
||||||
REQUIRE(extracted.size() == 0);
|
REQUIRE(extracted.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Marching squares directions", "[MarchingSquares]") {
|
TEST_CASE("Marching squares directions", "[MarchingSquares]")
|
||||||
marchsq::Coord crd{1, 1};
|
{
|
||||||
|
using namespace marchsq;
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::left).r == 1);
|
Coord crd{0, 0};
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::left).c == 0);
|
|
||||||
|
__impl::step(crd, __impl::Dir::left);
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::down).r == 2);
|
CHECK(crd == Coord(0, -1));
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::down).c == 1);
|
__impl::step(crd, __impl::Dir::down);
|
||||||
|
CHECK(crd == Coord(1, -1));
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::right).r == 1);
|
__impl::step(crd, __impl::Dir::right);
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::right).c == 2);
|
CHECK(crd == Coord(1, 0));
|
||||||
|
__impl::step(crd, __impl::Dir::up);
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::up).r == 0);
|
CHECK(crd == Coord(0, 0));
|
||||||
REQUIRE(step(crd, marchsq::__impl::Dir::up).c == 1);
|
__impl::step(crd, __impl::Dir::left, 7);
|
||||||
|
CHECK(crd == Coord(0, -7));
|
||||||
|
__impl::step(crd, __impl::Dir::down, 7);
|
||||||
|
CHECK(crd == Coord(7, -7));
|
||||||
|
__impl::step(crd, __impl::Dir::right, 7);
|
||||||
|
CHECK(crd == Coord(7, 0));
|
||||||
|
__impl::step(crd, __impl::Dir::up, 7);
|
||||||
|
CHECK(crd == Coord(0, 0));
|
||||||
|
__impl::step(crd, __impl::Dir::left, -3);
|
||||||
|
CHECK(crd == Coord(0, 3));
|
||||||
|
__impl::step(crd, __impl::Dir::down, -3);
|
||||||
|
CHECK(crd == Coord(-3, 3));
|
||||||
|
__impl::step(crd, __impl::Dir::right, -3);
|
||||||
|
CHECK(crd == Coord(-3, 0));
|
||||||
|
__impl::step(crd, __impl::Dir::up, -3);
|
||||||
|
CHECK(crd == Coord(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Fully covered raster should result in a rectangle", "[MarchingSquares]") {
|
TEST_CASE("Fully covered raster should result in a rectangle", "[MarchingSquares]")
|
||||||
|
{
|
||||||
auto rst = create_raster({4, 4}, 4., 4.);
|
auto rst = create_raster({4, 4}, 4., 4.);
|
||||||
|
|
||||||
ExPolygon rect = square(4);
|
ExPolygon rect = square(4);
|
||||||
|
|
||||||
SECTION("Full accuracy") {
|
SECTION("Full accuracy") { test_expolys(rst, {rect}, W1x1, "fully_covered_full_acc"); }
|
||||||
test_expolys(rst, {rect}, W2x2, "fully_covered_full_acc");
|
|
||||||
}
|
SECTION("Half accuracy") { test_expolys(rst, {rect}, W2x2, "fully_covered_half_acc"); }
|
||||||
|
|
||||||
SECTION("Half accuracy") {
|
|
||||||
test_expolys(rst, {rect}, W4x4, "fully_covered_half_acc");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("4x4 raster with one ring", "[MarchingSquares]") {
|
TEST_CASE("4x4 raster with one ring", "[MarchingSquares]")
|
||||||
|
{
|
||||||
sla::RasterBase::PixelDim pixdim{1, 1};
|
sla::PixelDim pixdim{1, 1};
|
||||||
|
|
||||||
// We need one additional row and column to detect edges
|
// We need one additional row and column to detect edges
|
||||||
sla::RasterGrayscaleAA rst{{4, 4}, pixdim, {}, agg::gamma_threshold(.5)};
|
sla::RasterGrayscaleAA rst{{4, 4}, pixdim, {}, agg::gamma_threshold(.5)};
|
||||||
|
|
||||||
// Draw a triangle from individual pixels
|
ExPolygons one = {{{1, 1}, {3, 1}, {3, 3}, {2, 3}, {2, 2}, {1, 2}}};
|
||||||
rst.draw(square(1., {0500000, 0500000}));
|
for (ExPolygon& p : one)
|
||||||
rst.draw(square(1., {1500000, 0500000}));
|
p.scale(scaled(1.0));
|
||||||
rst.draw(square(1., {2500000, 0500000}));
|
test_expolys(rst, one, W1x1, "one_4x4");
|
||||||
|
|
||||||
rst.draw(square(1., {1500000, 1500000}));
|
|
||||||
rst.draw(square(1., {2500000, 1500000}));
|
|
||||||
|
|
||||||
rst.draw(square(1., {2500000, 2500000}));
|
|
||||||
|
|
||||||
std::fstream out("4x4.png", std::ios::out);
|
|
||||||
out << rst.encode(sla::PNGRasterEncoder{});
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
ExPolygons extracted = sla::raster_to_polygons(rst);
|
|
||||||
|
|
||||||
SVG svg("4x4.svg");
|
|
||||||
svg.draw(extracted);
|
|
||||||
svg.Close();
|
|
||||||
|
|
||||||
REQUIRE(extracted.size() == 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("4x4 raster with two rings", "[MarchingSquares]") {
|
TEST_CASE("10x10 raster with two rings", "[MarchingSquares]")
|
||||||
|
{
|
||||||
sla::RasterBase::PixelDim pixdim{1, 1};
|
sla::PixelDim pixdim{1, 1};
|
||||||
|
|
||||||
// We need one additional row and column to detect edges
|
// We need one additional row and column to detect edges
|
||||||
sla::RasterGrayscaleAA rst{{5, 5}, pixdim, {}, agg::gamma_threshold(.5)};
|
sla::RasterGrayscaleAA rst{{10, 10}, pixdim, {}, agg::gamma_threshold(.5)};
|
||||||
|
|
||||||
SECTION("Ambiguous case with 'ac' square") {
|
SECTION("Ambiguous case with 'bd' square")
|
||||||
|
{
|
||||||
// Draw a triangle from individual pixels
|
ExPolygons ac = {{{1, 1}, {3, 1}, {3, 2}, {2, 2}, {2, 3}, {1, 3}}, {{4, 4}, {2, 4}, {2, 3}, {3, 3}, {3, 2}, {4, 2}}};
|
||||||
rst.draw(square(1., {3500000, 2500000}));
|
for (ExPolygon& p : ac)
|
||||||
rst.draw(square(1., {3500000, 3500000}));
|
p.scale(scaled(2.0));
|
||||||
rst.draw(square(1., {2500000, 3500000}));
|
test_expolys(rst, ac, W1x1, "bd_10x10", false);
|
||||||
|
|
||||||
rst.draw(square(1., {2500000, 1500000}));
|
|
||||||
rst.draw(square(1., {1500000, 1500000}));
|
|
||||||
rst.draw(square(1., {1500000, 2500000}));
|
|
||||||
|
|
||||||
std::fstream out("4x4_ac.png", std::ios::out);
|
|
||||||
out << rst.encode(sla::PNGRasterEncoder{});
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
ExPolygons extracted = sla::raster_to_polygons(rst);
|
|
||||||
|
|
||||||
SVG svg("4x4_ac.svg");
|
|
||||||
svg.draw(extracted);
|
|
||||||
svg.Close();
|
|
||||||
|
|
||||||
REQUIRE(extracted.size() == 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Ambiguous case with 'bd' square") {
|
SECTION("Ambiguous case with 'ac' square")
|
||||||
|
{
|
||||||
// Draw a triangle from individual pixels
|
ExPolygons bd = {{{1, 4}, {1, 2}, {2, 2}, {2, 3}, {3, 3}, {3, 4}}, {{4, 1}, {4, 3}, {3, 3}, {3, 2}, {2, 2}, {2, 1}}};
|
||||||
rst.draw(square(1., {3500000, 1500000}));
|
for (ExPolygon& p : bd)
|
||||||
rst.draw(square(1., {3500000, 2500000}));
|
p.scale(scaled(2.0));
|
||||||
rst.draw(square(1., {2500000, 1500000}));
|
test_expolys(rst, bd, W1x1, "ac_10x10", false);
|
||||||
|
|
||||||
rst.draw(square(1., {1500000, 2500000}));
|
|
||||||
rst.draw(square(1., {1500000, 3500000}));
|
|
||||||
rst.draw(square(1., {2500000, 3500000}));
|
|
||||||
|
|
||||||
std::fstream out("4x4_bd.png", std::ios::out);
|
|
||||||
out << rst.encode(sla::PNGRasterEncoder{});
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
ExPolygons extracted = sla::raster_to_polygons(rst);
|
|
||||||
|
|
||||||
SVG svg("4x4_bd.svg");
|
|
||||||
svg.draw(extracted);
|
|
||||||
svg.Close();
|
|
||||||
|
|
||||||
REQUIRE(extracted.size() == 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Square with hole in the middle", "[MarchingSquares]") {
|
TEST_CASE("Square with hole in the middle", "[MarchingSquares]")
|
||||||
|
{
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
ExPolygons inp = {square_with_hole(50.)};
|
ExPolygons inp = {square_with_hole(50.)};
|
||||||
|
|
||||||
SECTION("Proportional raster, 1x1 mm pixel size, full accuracy") {
|
SECTION("Proportional raster, 1x1 mm pixel size, full accuracy")
|
||||||
test_expolys(create_raster({100, 100}, 100., 100.), inp, W2x2, "square_with_hole_proportional_1x1_mm_px_full");
|
{
|
||||||
|
test_expolys(create_raster({100, 100}, 100., 100.), inp, W1x1, "square_with_hole_proportional_1x1_mm_px_full");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Proportional raster, 1x1 mm pixel size, half accuracy") {
|
SECTION("Proportional raster, 1x1 mm pixel size, half accuracy")
|
||||||
test_expolys(create_raster({100, 100}, 100., 100.), inp, W4x4, "square_with_hole_proportional_1x1_mm_px_half");
|
{
|
||||||
|
test_expolys(create_raster({100, 100}, 100., 100.), inp, W2x2, "square_with_hole_proportional_1x1_mm_px_half");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Landscape raster, 1x1 mm pixel size, full accuracy") {
|
SECTION("Landscape raster, 1x1 mm pixel size, full accuracy")
|
||||||
test_expolys(create_raster({150, 100}, 150., 100.), inp, W2x2, "square_with_hole_landsc_1x1_mm_px_full");
|
{
|
||||||
|
test_expolys(create_raster({150, 100}, 150., 100.), inp, W1x1, "square_with_hole_landsc_1x1_mm_px_full");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Landscape raster, 1x1 mm pixel size, half accuracy") {
|
SECTION("Landscape raster, 1x1 mm pixel size, half accuracy")
|
||||||
test_expolys(create_raster({150, 100}, 150., 100.), inp, W4x4, "square_with_hole_landsc_1x1_mm_px_half");
|
{
|
||||||
|
test_expolys(create_raster({150, 100}, 150., 100.), inp, W2x2, "square_with_hole_landsc_1x1_mm_px_half");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Portrait raster, 1x1 mm pixel size, full accuracy") {
|
SECTION("Portrait raster, 1x1 mm pixel size, full accuracy")
|
||||||
test_expolys(create_raster({100, 150}, 100., 150.), inp, W2x2, "square_with_hole_portrait_1x1_mm_px_full");
|
{
|
||||||
|
test_expolys(create_raster({100, 150}, 100., 150.), inp, W1x1, "square_with_hole_portrait_1x1_mm_px_full");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Portrait raster, 1x1 mm pixel size, half accuracy") {
|
SECTION("Portrait raster, 1x1 mm pixel size, half accuracy")
|
||||||
test_expolys(create_raster({100, 150}, 100., 150.), inp, W4x4, "square_with_hole_portrait_1x1_mm_px_half");
|
{
|
||||||
|
test_expolys(create_raster({100, 150}, 100., 150.), inp, W2x2, "square_with_hole_portrait_1x1_mm_px_half");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Proportional raster, 2x2 mm pixel size, full accuracy") {
|
SECTION("Proportional raster, 2x2 mm pixel size, full accuracy")
|
||||||
test_expolys(create_raster({200, 200}, 100., 100.), inp, W2x2, "square_with_hole_proportional_2x2_mm_px_full");
|
{
|
||||||
|
test_expolys(create_raster({50, 50}, 100., 100.), inp, W1x1, "square_with_hole_proportional_2x2_mm_px_full");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Proportional raster, 2x2 mm pixel size, half accuracy") {
|
SECTION("Proportional raster, 2x2 mm pixel size, half accuracy")
|
||||||
test_expolys(create_raster({200, 200}, 100., 100.), inp, W4x4, "square_with_hole_proportional_2x2_mm_px_half");
|
{
|
||||||
|
test_expolys(create_raster({50, 50}, 100., 100.), inp, W2x2, "square_with_hole_proportional_2x2_mm_px_half");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Proportional raster, 0.5x0.5 mm pixel size, full accuracy") {
|
SECTION("Proportional raster, 0.5x0.5 mm pixel size, full accuracy")
|
||||||
test_expolys(create_raster({50, 50}, 100., 100.), inp, W2x2, "square_with_hole_proportional_0.5x0.5_mm_px_full");
|
{
|
||||||
|
test_expolys(create_raster({200, 200}, 100., 100.), inp, W1x1, "square_with_hole_proportional_0.5x0.5_mm_px_full");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Proportional raster, 0.5x0.5 mm pixel size, half accuracy") {
|
SECTION("Proportional raster, 0.5x0.5 mm pixel size, half accuracy")
|
||||||
test_expolys(create_raster({50, 50}, 100., 100.), inp, W4x4, "square_with_hole_proportional_0.5x0.5_mm_px_half");
|
{
|
||||||
|
test_expolys(create_raster({200, 200}, 100., 100.), inp, W2x2, "square_with_hole_proportional_0.5x0.5_mm_px_half");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Circle with hole in the middle", "[MarchingSquares]") {
|
TEST_CASE("Circle with hole in the middle", "[MarchingSquares]")
|
||||||
|
{
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
test_expolys(create_raster({1000, 1000}), circle_with_hole(25.), W2x2, "circle_with_hole");
|
test_expolys(create_raster({1000, 1000}), circle_with_hole(25.), W1x1, "circle_with_hole");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recreate_object_from_rasters(const std::string &objname, float lh) {
|
static void recreate_object_from_rasters(const std::string& objname, float lh)
|
||||||
|
{
|
||||||
TriangleMesh mesh = load_model(objname);
|
TriangleMesh mesh = load_model(objname);
|
||||||
|
|
||||||
auto bb = mesh.bounding_box();
|
auto bb = mesh.bounding_box();
|
||||||
Vec3f tr = -bb.center().cast<float>();
|
Vec3f tr = -bb.center().cast<float>();
|
||||||
mesh.translate(tr.x(), tr.y(), tr.z());
|
mesh.translate(tr.x(), tr.y(), tr.z());
|
||||||
bb = mesh.bounding_box();
|
bb = mesh.bounding_box();
|
||||||
|
|
||||||
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
|
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
|
||||||
|
|
||||||
sla::RasterBase::Resolution res{2560, 1440};
|
sla::Resolution res{2560, 1440};
|
||||||
double disp_w = 120.96;
|
double disp_w = 120.96;
|
||||||
double disp_h = 68.04;
|
double disp_h = 68.04;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
size_t cntr = 0;
|
size_t cntr = 0;
|
||||||
#endif
|
#endif
|
||||||
for (ExPolygons &layer : layers) {
|
for (ExPolygons& layer : layers) {
|
||||||
auto rst = create_raster(res, disp_w, disp_h);
|
auto rst = create_raster(res, disp_w, disp_h);
|
||||||
|
|
||||||
for (ExPolygon &island : layer) {
|
for (ExPolygon& island : layer) {
|
||||||
rst.draw(island);
|
rst.draw(island);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out);
|
std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out);
|
||||||
out << rst.encode(sla::PNGRasterEncoder{});
|
out << rst.encode(sla::PNGRasterEncoder{});
|
||||||
out.close();
|
out.close();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ExPolygons layer_ = sla::raster_to_polygons(rst);
|
ExPolygons layer_ = sla::raster_to_polygons(rst);
|
||||||
// float delta = scaled(std::min(rst.pixel_dimensions().h_mm,
|
// float delta = scaled(std::min(rst.pixel_dimensions().h_mm,
|
||||||
// rst.pixel_dimensions().w_mm)) / 2;
|
// rst.pixel_dimensions().w_mm)) / 2;
|
||||||
|
|
||||||
// layer_ = expolygons_simplify(layer_, delta);
|
// layer_ = expolygons_simplify(layer_, delta);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
SVG svg(objname + std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)}));
|
SVG svg(objname + std::to_string(cntr) + ".svg", rstBBox(rst));
|
||||||
svg.draw(layer_);
|
svg.draw(layer_);
|
||||||
svg.draw(layer, "green");
|
svg.draw(layer, "green");
|
||||||
svg.Close();
|
svg.Close();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
double layera = 0., layera_ = 0.;
|
double layera = 0., layera_ = 0.;
|
||||||
for (auto &p : layer) layera += p.area();
|
for (auto& p : layer)
|
||||||
for (auto &p : layer_) layera_ += p.area();
|
layera += p.area();
|
||||||
|
for (auto& p : layer_)
|
||||||
|
layera_ += p.area();
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::cout << cntr++ << std::endl;
|
std::cout << cntr++ << std::endl;
|
||||||
#endif
|
#endif
|
||||||
double diff = std::abs(layera_ - layera);
|
double diff = std::abs(layera_ - layera);
|
||||||
REQUIRE((diff <= 0.1 * layera || diff < scaled<double>(1.) * scaled<double>(1.)));
|
REQUIRE((diff <= 0.1 * layera || diff < scaled<double>(1.) * scaled<double>(1.)));
|
||||||
|
|
||||||
layer = std::move(layer_);
|
layer = std::move(layer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set out = slices_to_mesh(layers, bb.min.z(), double(lh), double(lh));
|
indexed_triangle_set out = slices_to_mesh(layers, bb.min.z(), double(lh), double(lh));
|
||||||
|
|
||||||
its_write_obj(out, "out_from_rasters.obj");
|
its_write_obj(out, "out_from_rasters.obj");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Recreate object from rasters", "[SL1Import]") {
|
TEST_CASE("Recreate object from rasters", "[SL1Import]") { recreate_object_from_rasters("frog_legs.obj", 0.05f); }
|
||||||
recreate_object_from_rasters("frog_legs.obj", 0.05f);
|
|
||||||
|
namespace marchsq {
|
||||||
|
|
||||||
|
static constexpr float layerf = 0.20; // layer height in mm (used for z values).
|
||||||
|
static constexpr float gsizef = 100.0; // grid size in mm (box volume side length).
|
||||||
|
static constexpr float wsizef = 0.50; // grid window size in mm (roughly line segment length).
|
||||||
|
static constexpr float psizef = 0.01; // raster pixel size in mm (roughly point accuracy).
|
||||||
|
static constexpr float isoval = 0.0; // iso value threshold to use.
|
||||||
|
static constexpr size_t wsize = std::round(wsizef / psizef);
|
||||||
|
|
||||||
|
static float period = 10.0; // gyroid "wavelength" in mm (2x line spacing).
|
||||||
|
static float freq = 2 * PI / period; // gyroid frequency in waves per mm.
|
||||||
|
|
||||||
|
void set_period(float len = 10.0)
|
||||||
|
{
|
||||||
|
period = len;
|
||||||
|
freq = 2 * PI / period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t layer_n;
|
||||||
|
static size_t ring_n;
|
||||||
|
static size_t point_n;
|
||||||
|
static size_t get_n;
|
||||||
|
|
||||||
|
void reset_stats()
|
||||||
|
{
|
||||||
|
layer_n = 0;
|
||||||
|
ring_n = 0;
|
||||||
|
point_n = 0;
|
||||||
|
get_n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
using Rings = std::vector<Ring>;
|
||||||
|
|
||||||
|
template<> struct _RasterTraits<size_t>
|
||||||
|
{
|
||||||
|
// using Rst = Slic3r::sla::RasterGrayscaleAA;
|
||||||
|
// The type of pixel cell in the raster
|
||||||
|
using ValueType = float;
|
||||||
|
|
||||||
|
// Value at a given position
|
||||||
|
static float get(const size_t& layer, size_t row, size_t col)
|
||||||
|
{
|
||||||
|
get_n++;
|
||||||
|
const float x = col * psizef * freq;
|
||||||
|
const float y = row * psizef * freq;
|
||||||
|
const float z = layer * psizef * freq;
|
||||||
|
|
||||||
|
return sinf(x) * cosf(y) + sinf(y) * cosf(z) + sinf(z) * cosf(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of rows and cols of the raster
|
||||||
|
static size_t rows(const size_t& layer) { return std::round(gsizef / psizef); }
|
||||||
|
static size_t cols(const size_t& layer) { return std::round(gsizef / psizef); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Rings get_gyroids(size_t l)
|
||||||
|
{
|
||||||
|
size_t layer = l;
|
||||||
|
Rings rings = execute(layer, isoval, {wsize, wsize});
|
||||||
|
layer_n++;
|
||||||
|
ring_n += rings.size();
|
||||||
|
for (auto r : rings)
|
||||||
|
point_n += r.size();
|
||||||
|
return rings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace marchsq
|
||||||
|
|
||||||
|
void benchmark_gyroid(float period)
|
||||||
|
{
|
||||||
|
marchsq::reset_stats();
|
||||||
|
marchsq::set_period(period);
|
||||||
|
INFO("grid size: " << marchsq::gsizef << "mm\nlayer height: " << marchsq::layerf << "mm\n");
|
||||||
|
INFO("window size: " << marchsq::wsizef << "mm\npoint size: " << marchsq::psizef << "mm\n");
|
||||||
|
INFO("gyroid period: " << marchsq::period << "mm\n");
|
||||||
|
BENCHMARK("indexed", i) { return marchsq::get_gyroids(i); };
|
||||||
|
INFO("output avg rings/layer: " << float(marchsq::ring_n) / float(marchsq::layer_n) << "\n");
|
||||||
|
INFO("output avg points/layer: " << float(marchsq::point_n) / float(marchsq::layer_n) << "\n");
|
||||||
|
INFO("output avg gets/layer: " << float(marchsq::get_n) / float(marchsq::layer_n) << "\n");
|
||||||
|
|
||||||
|
REQUIRE(marchsq::layer_n > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Benchmark gyroid cube period 10.0mm", "[MarchingSquares]") { benchmark_gyroid(10.0); }
|
||||||
|
|
||||||
|
TEST_CASE("Benchmark gyroid cube period 5.0mm", "[MarchingSquares]") { benchmark_gyroid(5.0); }
|
||||||
|
|||||||