Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2020-12-30 02:14:11 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2020-12-30 02:14:11 +0300
commit7211afe54d7a6863d1aefda4cbd0034659a7a11d (patch)
tree644a7f2c311b87987d52378b9aae357945d759f7
parentb2617d149f1528c5ad292dacfae73de245ab07a9 (diff)
v2.1.0v2.1.0
-rw-r--r--CHANGELOG.md21
-rw-r--r--CREDITS.md5
-rw-r--r--CreateRelease.WPF.ps165
-rw-r--r--README.md4
-rw-r--r--UVtools.CAD/UVtools.icnsbin0 -> 25498 bytes
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs7
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs2
-rw-r--r--UVtools.Core/FileFormats/PhotonWorkshopFile.cs12
-rw-r--r--UVtools.Core/Layer/Layer.cs651
-rw-r--r--UVtools.Core/Layer/LayerManager.cs1316
-rw-r--r--UVtools.Core/Operations/Operation.cs12
-rw-r--r--UVtools.Core/Operations/OperationArithmetic.cs83
-rw-r--r--UVtools.Core/Operations/OperationBlur.cs64
-rw-r--r--UVtools.Core/Operations/OperationCalibrateElephantFoot.cs63
-rw-r--r--UVtools.Core/Operations/OperationCalibrateGrayscale.cs73
-rw-r--r--UVtools.Core/Operations/OperationCalibrateTolerance.cs46
-rw-r--r--UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs46
-rw-r--r--UVtools.Core/Operations/OperationChangeResolution.cs62
-rw-r--r--UVtools.Core/Operations/OperationEditParameters.cs17
-rw-r--r--UVtools.Core/Operations/OperationFlip.cs64
-rw-r--r--UVtools.Core/Operations/OperationInfill.cs204
-rw-r--r--UVtools.Core/Operations/OperationLayerClone.cs53
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs70
-rw-r--r--UVtools.Core/Operations/OperationLayerReHeight.cs75
-rw-r--r--UVtools.Core/Operations/OperationLayerRemove.cs66
-rw-r--r--UVtools.Core/Operations/OperationMask.cs39
-rw-r--r--UVtools.Core/Operations/OperationMorph.cs59
-rw-r--r--UVtools.Core/Operations/OperationMove.cs186
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs70
-rw-r--r--UVtools.Core/Operations/OperationPixelDimming.cs104
-rw-r--r--UVtools.Core/Operations/OperationRaftRelief.cs191
-rw-r--r--UVtools.Core/Operations/OperationRedrawModel.cs249
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs220
-rw-r--r--UVtools.Core/Operations/OperationResize.cs100
-rw-r--r--UVtools.Core/Operations/OperationRotate.cs56
-rw-r--r--UVtools.Core/Operations/OperationSolidify.cs63
-rw-r--r--UVtools.Core/Operations/OperationThreshold.cs51
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.Platforms/arch-x64/libcvextern.sobin0 -> 99097232 bytes
-rw-r--r--UVtools.Platforms/linux-x64/libcvextern.so (renamed from UVtools.WPF/libcvextern.so)bin45592592 -> 45592592 bytes
-rw-r--r--UVtools.Platforms/osx-x64/Info.plist26
-rw-r--r--UVtools.Platforms/osx-x64/libcvextern.dylib (renamed from UVtools.WPF/libcvextern.dylib)bin48698852 -> 48698852 bytes
-rw-r--r--UVtools.Platforms/rhel-x64/libcvextern.sobin0 -> 45592592 bytes
-rw-r--r--UVtools.WPF/App.axaml.cs9
-rw-r--r--UVtools.WPF/Assets/Icons/code-branch-16x16.pngbin0 -> 187 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/sun-16x16.pngbin0 -> 198 bytes
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml87
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml66
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs45
-rw-r--r--UVtools.WPF/MainWindow.Issues.cs2
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs169
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs123
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs2
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj8
-rw-r--r--UVtools.WPF/UVtools.sh2
55 files changed, 2675 insertions, 2335 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4e6515..ec76614 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
# Changelog
-## 25/11/2020 - v2.0.0
+## 29/12/2020 - v2.1.0
+
+* (Add) Tool - Redraw model/supports: Redraw the model or supports with a set brightness.
+ This requires an extra sliced file from same object but without any supports and raft, straight to the build plate.
+* (Add) Tool - Raft Relief:
+ * Allow ignore a number of layer(s) to start only after that number, detault is 0
+ * Allow set a pixel brightness for the operation, detault is 0
+ * New "dimming" type, works like relief but instead of drill raft it set to a brightness level
+* (Add) Arch-x64 package (#104)
+* (Fix) A OS dependent startup crash when there's no primary screen set (#115)
+* (Fix) Tool - Re height: Able to cancel the job
+* (Fix) Unable to save "Calibration - Tolerance" profiles
+* (Change) Core: Move all operations code from LayerManager and Layer to it own Operation* class within a Execute method (Abstraction)
+* (Change) sh UVtools.sh to run independent UVtools instance first, if not found it will fallback to dotnet UVtools.dll
+* (Change) Compile and zip project with WSL to keep the +x (execute) attribute for linux and unix systems
+* (Change) MacOS builds are now packed as an application bundle (Auto-updater disabled for now)
+* (Remove) Universal package from builds/releases
+
+## 25/12/2020 - v2.0.0
This release bump the major version due the introduction of .NET 5.0, the discontinuation old UVtools GUI project and the new calibration wizards.
* (Upgrade) From .NET Core 3.1 to .NET 5.0
@@ -11,7 +29,6 @@ This release bump the major version due the introduction of .NET 5.0, the discon
* **Take into account the screen scale factor to limit the dialogs windows maximum size**: Due wrong information UVtools can clamp the windows maximum size when you have plenty more avaliable or when use in a secondary monitor. If is the case disable this option
* **Horizontal limiting margin:** Limits windows and dialogs maximum width to the screen resolution less this margin
* **Vertical limiting margin:** Limits windows and dialogs maximum height to the screen resolution less this margin
-* (Add) Setting - General: Take into account the screen scale factor to limit the dialogs windows maximum size. Due wrong information UVtools can cap the windows maximum size when you have plenty more avaliable or when use in a secondary monitor. If is the case disable this option
* (Add) Ctrl + Shift + Z to undo and edit the last operation (If contain a valid operation)
* (Add) Allow to deselect the current selected profile
* (Add) Allow to set a default profile to load in when open a tool
diff --git a/CREDITS.md b/CREDITS.md
index 22e44b0..13a83c2 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -20,7 +20,7 @@
### Jason S. McMullan (ezrec)
-* For ideas and permission to use his project (uv3dp)
+* For ideas, file formats and permission to use his project (uv3dp)
* https://github.com/ezrec/uv3dp
# Supporters / Contributors
@@ -40,4 +40,5 @@
* Nahin Mulla
* Jorge diego Robles Ayerbe
* Timothy Gray
-* David Gordon \ No newline at end of file
+* David Gordon
+* Claus Pfeilschifter \ No newline at end of file
diff --git a/CreateRelease.WPF.ps1 b/CreateRelease.WPF.ps1
index 56da287..9ceec3e 100644
--- a/CreateRelease.WPF.ps1
+++ b/CreateRelease.WPF.ps1
@@ -29,7 +29,7 @@ class FixedEncoder : System.Text.UTF8Encoding {
####################################
### Configuration ###
####################################
-$enableMSI = $false
+$enableMSI = $true
# Profilling
$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
$deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
@@ -44,7 +44,11 @@ $project = "UVtools.WPF"
$buildWith = "Release"
$netFolder = "net5.0"
$releaseFolder = "$PSScriptRoot\$project\bin\$buildWith\$netFolder"
-$publishFolder = "$PSScriptRoot\publish"
+$objFolder = "$PSScriptRoot\$project\obj\$buildWith\$netFolder"
+$publishFolder = "publish"
+$platformsFolder = "UVtools.Platforms"
+
+$macIcns = "UVtools.CAD/UVtools.icns"
#$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion
$projectXml = [Xml] (Get-Content "$PSScriptRoot\$project\$project.csproj")
@@ -80,14 +84,22 @@ $runtimes =
"win-x64" = @{
"extraCmd" = "-p:PublishReadyToRun=true"
"exclude" = @("libcvextern.so", "libcvextern.dylib", "UVtools.sh")
+ "include" = @()
}
"linux-x64" = @{
"extraCmd" = "-p:PublishReadyToRun=true"
"exclude" = @("x86", "x64", "libcvextern.dylib")
+ "include" = @("libcvextern.so")
+ }
+ "arch-x64" = @{
+ "extraCmd" = "-p:PublishReadyToRun=true"
+ "exclude" = @("x86", "x64", "libcvextern.dylib", "libcvextern.so")
+ "include" = @("libcvextern.so")
}
"rhel-x64" = @{
"extraCmd" = "-p:PublishReadyToRun=true"
"exclude" = @("x86", "x64", "libcvextern.dylib")
+ "include" = @("libcvextern.so")
}
#"unix-x64" = @{
# "extraCmd" = "-p:PublishReadyToRun=true"
@@ -96,6 +108,7 @@ $runtimes =
"osx-x64" = @{
"extraCmd" = "-p:PublishReadyToRun=true"
"exclude" = @("x86", "x64", "libcvextern.so")
+ "include" = @("libcvextern.dylib")
}
}
@@ -104,31 +117,65 @@ foreach ($obj in $runtimes.GetEnumerator()) {
$deployStopWatch.Restart()
$runtime = $obj.Name; # runtime name
$extraCmd = $obj.extraCmd; # extra cmd to run with dotnet
- $targetZip = "$publishFolder\${software}_${runtime}_v$version.zip" # Target zip filename
+ $targetZip = "$publishFolder/${software}_${runtime}_v$version.zip" # Target zip filename
# Deploy
Write-Output "################################
Building: $runtime"
- dotnet publish $project -o "$publishFolder\$runtime" -c $buildWith -r $runtime $extraCmd
+ wsl dotnet publish $project -o "$publishFolder/$runtime" -c $buildWith -r $runtime $extraCmd
# Cleanup
- Remove-Item "$releaseFolder\$runtime" -Recurse -ErrorAction Ignore
+ Remove-Item "$releaseFolder\$runtime" -Recurse -ErrorAction Ignore
+ Remove-Item "$objFolder\$runtime" -Recurse -ErrorAction Ignore
+ Write-Output "$releaseFolder\$runtime"
+
foreach ($excludeObj in $obj.Value.exclude) {
Write-Output "Excluding: $excludeObj"
Remove-Item "$publishFolder\$runtime\$excludeObj" -Recurse -ErrorAction Ignore
}
- # Zip
+ foreach ($includeObj in $obj.Value.include) {
+ Write-Output "Including: $includeObj"
+ Copy-Item "$platformsFolder\$runtime\$includeObj" -Destination "$publishFolder\$runtime" -Recurse -ErrorAction Ignore
+ }
+
Write-Output "Compressing $runtime to: $targetZip"
- [System.IO.Compression.ZipFile]::CreateFromDirectory("$publishFolder\$runtime", $targetZip, [System.IO.Compression.CompressionLevel]::Optimal, $false, [FixedEncoder]::new())
- $deployStopWatch.Stop()
+ Write-Output $targetZip "$publishFolder/$runtime"
+
+ if($runtime.Equals('osx-x64')){
+ $macAppFolder = "${software}.app"
+ $macPublishFolder = "$publishFolder/${macAppFolder}"
+
+ wsl mkdir "$macPublishFolder"
+ wsl mkdir "$macPublishFolder/Contents"
+ wsl mkdir "$macPublishFolder/Contents/MacOS"
+ wsl mkdir "$macPublishFolder/Contents/Resources"
+
+ $outputInfoplist = "$macPublishFolder/Contents/Info.plist"
+ wsl cp "$platformsFolder/$runtime/Info.plist" "$outputInfoplist"
+ ((Get-Content -Path "$outputInfoplist") -replace '#VERSION',"$version") | Set-Content -Path "$outputInfoplist"
+ wsl cp "$macIcns" "$macPublishFolder/Contents/Resources/$software.icns"
+ wsl cp -a "$publishFolder/$runtime/." "$macPublishFolder/Contents/MacOS"
+
+ wsl cd "$publishFolder/" `&`& pwd `&`& zip -r "../$targetZip" "$macAppFolder/*"
+ }
+ else {
+ wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$targetZip" "."
+ }
+ # Zip
+ #Write-Output "Compressing $runtime to: $targetZip"
+ #Write-Output $targetZip "$publishFolder/$runtime"
+ #[System.IO.Compression.ZipFile]::CreateFromDirectory("$publishFolder\$runtime", $targetZip, [System.IO.Compression.CompressionLevel]::Optimal, $false, [FixedEncoder]::new())
+ #wsl cd "$publishFolder/$runtime" `&`& pwd `&`& chmod +x -f "./$software" `|`| : `&`& zip -r "../../$targetZip" "."
+ $deployStopWatch.Stop()
Write-Output "Took: $($deployStopWatch.Elapsed)
################################
"
}
# Universal package
+<#
$deployStopWatch.Restart()
$runtime = "universal-x86-x64"
$targetZip = "$publishFolder\${software}_${runtime}_v$version.zip"
@@ -143,7 +190,7 @@ Write-Output "Took: $($deployStopWatch.Elapsed)
################################
"
$stopWatch.Stop()
-
+#>
# MSI Installer for Windows
if($enableMSI)
diff --git a/README.md b/README.md
index cec8208..0ee0652 100644
--- a/README.md
+++ b/README.md
@@ -181,7 +181,7 @@ sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y apt-transport-https
-dotnet-runtime-3.1
+dotnet-runtime-5.0
!-->
```bash
@@ -211,7 +211,7 @@ To run UVtools open it folder on a terminal and call one of:
1. 4GB RAM or higher
<!---
-* Donwload and install: https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.402-macos-x64-installer
+* Donwload and install: https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.101-macos-x64-installer
brew install libjpeg libpng libgeotiff libdc1394 ffmpeg openexr tbb
-->
diff --git a/UVtools.CAD/UVtools.icns b/UVtools.CAD/UVtools.icns
new file mode 100644
index 0000000..7ae55fa
--- /dev/null
+++ b/UVtools.CAD/UVtools.icns
Binary files differ
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index d9c1bc0..a48a5c7 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -248,5 +248,12 @@ namespace UVtools.Core.Extensions
return mat;
}
+ public static Mat InitMat(Size size, MCvScalar scalar, int channels = 1, DepthType depthType = DepthType.Cv8U)
+ {
+ var mat = new Mat(size, depthType, channels);
+ mat.SetTo(scalar);
+ return mat;
+ }
+
}
}
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 3bd20c5..21eef2f 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -565,7 +565,7 @@ namespace UVtools.Core.FileFormats
if (SuppressRebuildProperties) return;
if (e.PropertyName == nameof(LayerCount))
{
- if (this[LayerCount - 1] is null) return; // Not initialized
+ if (LayerCount == 0 || this[LayerCount - 1] is null) return; // Not initialized
LayerManager.RebuildLayersProperties();
RebuildGCode();
PrintTime = PrintTimeComputed;
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index 445d6b4..0fbaee3 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -772,12 +772,12 @@ namespace UVtools.Core.FileFormats
public override FileExtension[] FileExtensions { get; } = {
- new FileExtension("pwmx", "Photon Mono X (PWMX)"),
- new FileExtension("pwms", "Photon Mono SE (PWMS)"),
- new FileExtension("pwmo", "Photon Mono (PWMO)"),
- new FileExtension("pwx", "Photon X (PWX)"),
- new FileExtension("pws", "Photon / Photon S (PWS)"),
- new FileExtension("pw0", "Photon Zero (PW0)"),
+ new("pwmx", "Photon Mono X (PWMX)"),
+ new("pwms", "Photon Mono SE (PWMS)"),
+ new("pwmo", "Photon Mono (PWMO)"),
+ new("pwx", "Photon X (PWX)"),
+ new("pws", "Photon / Photon S (PWS)"),
+ new("pw0", "Photon Zero (PW0)"),
};
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index 3aa1fc1..875ca1b 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -210,14 +210,10 @@ namespace UVtools.Core
}
set
{
- using (var vector = new VectorOfByte())
- {
- CvInvoke.Imencode(".png", value, vector);
- CompressedBytes = vector.ToArray();
-
- GetBoundingRectangle(value, true);
- }
-
+ using var vector = new VectorOfByte();
+ CvInvoke.Imencode(".png", value, vector);
+ CompressedBytes = vector.ToArray();
+ GetBoundingRectangle(value, true);
RaisePropertyChanged();
}
}
@@ -633,646 +629,9 @@ namespace UVtools.Core
return result;
}
- public void Move(OperationMove operation)
- {
- using (var mat = LayerMat)
- {
- if (operation.ImageWidth == 0) operation.ImageWidth = (uint)mat.Width;
- if (operation.ImageHeight == 0) operation.ImageHeight = (uint)mat.Height;
-
- /*layer.Transform(1.0, 1.0, move.MarginLeft - move.MarginRight, move.MarginTop-move.MarginBottom);
- LayerMat = layer;*/
- /*using (var layerRoi = new Mat(layer, operation.SrcRoi))
- using (var dstLayer = layer.CloneBlank())
- using (var dstRoi = new Mat(dstLayer, operation.DstRoi))
- {
- layerRoi.CopyTo(dstRoi);
- LayerMat = dstLayer;
- }*/
-
- using (var srcRoi = new Mat(mat, operation.ROI))
- using (var dstRoi = new Mat(mat, operation.DstRoi))
- {
- if (operation.IsCutMove)
- {
- using (var targetRoi = srcRoi.Clone())
- {
- srcRoi.SetTo(new MCvScalar(0));
- targetRoi.CopyTo(dstRoi);
- }
- }
- else
- {
- srcRoi.CopyTo(dstRoi);
- }
-
- LayerMat = mat;
- }
- }
- }
-
-
- public void Resize(double xScale, double yScale, OperationResize operation)
- {
- using (var mat = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(mat);
- target.TransformFromCenter(xScale, yScale);
- LayerMat = mat;
- }
- }
-
- public void Flip(OperationFlip operation)
- {
- using (var mat = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(mat);
-
- if (operation.MakeCopy)
- {
- using (Mat dst = new Mat())
- {
-
- CvInvoke.Flip(target, dst, operation.FlipTypeOpenCV);
- CvInvoke.Add(target, dst, target);
- }
- }
- else
- {
- CvInvoke.Flip(target, target, operation.FlipTypeOpenCV);
- }
-
- LayerMat = mat;
- }
- }
-
- public void Rotate(OperationRotate operation)
- {
- using (var mat = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(mat);
- target.Rotate((double) operation.AngleDegrees);
- LayerMat = mat;
- }
- }
-
- public void Solidify(OperationSolidify operation)
- {
- using (Mat mat = LayerMat)
- {
- using (Mat filteredMat = new Mat())
- {
- Mat target = operation.GetRoiOrDefault(mat);
-
-
- CvInvoke.Threshold(target, filteredMat, 127, 255, ThresholdType.Binary); // Clean AA
-
- using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
- {
- using (Mat hierarchy = new Mat())
- {
- CvInvoke.FindContours(filteredMat, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple);
- var arr = hierarchy.GetData();
- for (int i = 0; i < contours.Size; i++)
- {
- if ((int)arr.GetValue(0, i, 2) != -1 || (int)arr.GetValue(0, i, 3) == -1) continue;
- if (operation.MinimumArea >= 1)
- {
- var rectangle = CvInvoke.BoundingRectangle(contours[i]);
- if (operation.AreaCheckType == OperationSolidify.AreaCheckTypes.More)
- {
- if (rectangle.GetArea() < operation.MinimumArea) continue;
- }
- else
- {
- if (rectangle.GetArea() > operation.MinimumArea) continue;
- }
-
- }
-
- CvInvoke.DrawContours(target, contours, i, new MCvScalar(255), -1);
- }
- }
- }
- }
-
- LayerMat = mat;
- }
- }
-
- public void Mask(OperationMask operation)
- {
- using (var mat = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(mat);
- if(operation.Mask.Size != target.Size) return;
- CvInvoke.BitwiseAnd(target, operation.Mask, target);
- LayerMat = mat;
- }
- }
-
- /*public void MutatePixelDimming(Matrix<byte> evenPattern = null, Matrix<byte> oddPattern = null, ushort borderSize = 5)
- {
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- if (ReferenceEquals(evenPattern, null))
- {
- evenPattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 127,
- [0, 1] = 255,
- [1, 0] = 255,
- [1, 1] = 127,
- };
-
- if (ReferenceEquals(oddPattern, null))
- {
- oddPattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 255,
- [0, 1] = 127,
- [1, 0] = 127,
- [1, 1] = 255,
- };
- }
- }
-
- using (Mat dst = LayerMat)
- {
- using (Mat erode = new Mat())
- {
- using (Mat diff = new Mat())
- {
- using (Mat mask = dst.CloneBlank())
- {
- CvInvoke.Erode(dst, erode, kernel, anchor, borderSize, BorderType.Reflect101, default);
- CvInvoke.Subtract(dst, erode, diff);
-
- if (Index % 2 == 0)
- {
- CvInvoke.Repeat(evenPattern, dst.Rows / evenPattern.Rows + 1, dst.Cols / evenPattern.Cols + 1, mask);
- }
- else
- {
- CvInvoke.Repeat(oddPattern, dst.Rows / oddPattern.Rows + 1, dst.Cols / oddPattern.Cols + 1, mask);
- }
-
- using (var maskReshape = new Mat(mask, new Rectangle(0, 0, dst.Width, dst.Height)))
- {
- CvInvoke.BitwiseAnd(erode, maskReshape, dst);
- }
-
- CvInvoke.Add(dst, diff, dst);
- LayerMat = dst;
- }
- }
- }
- }
- }*/
-
- public void PixelDimming(OperationPixelDimming operation, Mat patternMask, Mat alternatePatternMask = null)
- {
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
-
- if (ReferenceEquals(alternatePatternMask, null))
- {
- alternatePatternMask = patternMask;
- }
-
- int wallThickness = LayerManager.MutateGetIterationChamfer(
- Index,
- operation.LayerIndexStart,
- operation.LayerIndexEnd,
- (int)operation.WallThicknessStart,
- (int)operation.WallThicknessEnd,
- operation.Chamfer
- );
-
-
- using (Mat dst = LayerMat)
- using (Mat erode = new Mat())
- using (Mat diff = new Mat())
- {
- Mat target = operation.GetRoiOrDefault(dst);
-
-
- CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
- CvInvoke.Subtract(target, erode, diff);
-
-
- if (operation.WallsOnly)
- {
- CvInvoke.BitwiseAnd(diff, operation.IsNormalPattern(Index) ? patternMask : alternatePatternMask, target);
- CvInvoke.Add(erode, target, target);
- }
- else
- {
- CvInvoke.BitwiseAnd(erode, operation.IsNormalPattern(Index) ? patternMask : alternatePatternMask, target);
- CvInvoke.Add(target, diff, target);
- }
-
- LayerMat = dst;
- }
- }
-
- public void Infill(OperationInfill operation)
- {
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
-
- uint layerIndex = Index - operation.LayerIndexStart;
- var infillColor = new MCvScalar(operation.InfillBrightness);
-
- Mat patternMask = null;
- using (Mat dst = LayerMat)
- using (Mat erode = new Mat())
- using (Mat diff = new Mat())
- {
- Mat target = operation.GetRoiOrDefault(dst);
-
- /*if (operation.InfillType == OperationInfill.InfillAlgorithm.Rhombus)
- {
- const double rotationAngle = 55;
- patternMask = target.CloneBlank();
- int offsetLayerPos = (int)(layerIndex % operation.InfillSpacing);
- var pivot = new Point( operation.InfillThickness / 2, operation.InfillThickness / 2);
-
- Point[] points1 = {
- new Point(operation.InfillThickness / 4, operation.InfillThickness / 2), // Left
- new Point(operation.InfillThickness / 2, 0), // Top
- new Point((int) (operation.InfillThickness / 1.25), operation.InfillThickness / 2), // Right
- new Point(operation.InfillThickness / 2, operation.InfillThickness) // Bottom
- };
- var vec1 = new VectorOfPoint(points1);
-
-
- Point[] points2 = {
- points1[0].Rotate(rotationAngle, pivot),
- points1[1].Rotate(rotationAngle, pivot),
- points1[2].Rotate(rotationAngle, pivot),
- points1[3].Rotate(rotationAngle, pivot),
- };
- var vec2 = new VectorOfPoint(points2);
-
- Point[] points3 = {
- points1[0].Rotate(-rotationAngle, pivot),
- points1[1].Rotate(-rotationAngle, pivot),
- points1[2].Rotate(-rotationAngle, pivot),
- points1[3].Rotate(-rotationAngle, pivot),
- };
- var vec3 = new VectorOfPoint(points3);
-
-
- /*int halfPos = (operation.InfillThickness + offsetPos) / 2;
-
- Point[] points = {
- new Point(0+offsetPos, halfPos), // Left
- new Point(halfPos, 0+offsetPos), // Top
- new Point(operation.InfillThickness+offsetPos, halfPos), // Right
- new Point(halfPos, operation.InfillThickness+offsetPos) // Bottom
- }; */
- /* for (int y = 0; y < patternMask.Height; y += operation.InfillSpacing)
- {
- for (int x = 0; x < patternMask.Width; x+=operation.InfillSpacing)
- {
- CvInvoke.FillPoly(patternMask, vec1, infillColor, LineType.EightConnected, default, new Point(x, y+offsetLayerPos));
- CvInvoke.FillPoly(patternMask, vec2, infillColor, LineType.EightConnected, default, new Point(x- offsetLayerPos, y+ offsetLayerPos));
- CvInvoke.FillPoly(patternMask, vec3, infillColor, LineType.EightConnected, default, new Point(x+ offsetLayerPos, y+ offsetLayerPos));
- }
- }
-
- patternMask.Save("D:\\mask.png");
-
-
- }
- else*/ if (operation.InfillType == OperationInfill.InfillAlgorithm.Cubic ||
- operation.InfillType == OperationInfill.InfillAlgorithm.CubicCenterLink ||
- operation.InfillType == OperationInfill.InfillAlgorithm.CubicDynamicLink ||
- operation.InfillType == OperationInfill.InfillAlgorithm.CubicInterlinked)
- {
- using (var infillPattern = EmguExtensions.InitMat(new Size(operation.InfillSpacing, operation.InfillSpacing)))
- using (Mat matPattern = dst.CloneBlank())
- {
- bool firstPattern = true;
- uint accumulator = 0;
- uint step = 0;
- bool dynamicCenter = false;
- while (accumulator < layerIndex)
- {
- dynamicCenter = !dynamicCenter;
- firstPattern = true;
- accumulator += operation.InfillSpacing;
-
- if (accumulator >= layerIndex) break;
- firstPattern = false;
- accumulator += operation.InfillThickness;
- }
-
- if (firstPattern)
- {
- int thickness = operation.InfillThickness / 2;
- // Top Left
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, 0, thickness, thickness),
- infillColor, -1);
-
- // Top Right
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(infillPattern.Width - thickness, 0, thickness, thickness),
- infillColor, -1);
-
- // Bottom Left
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, infillPattern.Height - thickness, thickness, thickness),
- infillColor, -1);
-
- // Bottom Right
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(infillPattern.Width - thickness, infillPattern.Height - thickness,
- thickness, thickness),
- infillColor, -1);
-
- // Center cross
- int margin = (int)(operation.InfillSpacing - accumulator + layerIndex) - thickness;
- int marginInv = (int)(accumulator - layerIndex) - thickness;
-
- if (operation.InfillType == OperationInfill.InfillAlgorithm.CubicCenterLink ||
- (operation.InfillType == OperationInfill.InfillAlgorithm.CubicDynamicLink &&
- dynamicCenter) ||
- operation.InfillType == OperationInfill.InfillAlgorithm.CubicInterlinked)
- {
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, margin, operation.InfillThickness, operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, marginInv, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, marginInv, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, margin, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
- }
-
-
- if (operation.InfillType == OperationInfill.InfillAlgorithm.CubicInterlinked ||
- (operation.InfillType == OperationInfill.InfillAlgorithm.CubicDynamicLink && !dynamicCenter))
- {
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, -thickness, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, -thickness, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(-thickness, margin, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(-thickness, marginInv, operation.InfillThickness,
- operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(operation.InfillSpacing - thickness, margin,
- operation.InfillThickness, operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(operation.InfillSpacing - thickness, marginInv,
- operation.InfillThickness, operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, operation.InfillSpacing - thickness,
- operation.InfillThickness, operation.InfillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, operation.InfillSpacing - thickness,
- operation.InfillThickness, operation.InfillThickness),
- infillColor, -1);
- }
-
-
- }
- else
- {
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, 0, operation.InfillSpacing, operation.InfillSpacing),
- infillColor, operation.InfillThickness);
- }
-
-
- {
- CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
- target.Cols / infillPattern.Cols + 1, matPattern);
- patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
- }
- }
- }
-
-
- CvInvoke.Erode(target, erode, kernel, anchor, operation.WallThickness, BorderType.Reflect101,
- default);
- CvInvoke.Subtract(target, erode, diff);
-
-
- CvInvoke.BitwiseAnd(erode, patternMask, target);
- CvInvoke.Add(target, diff, target);
- patternMask?.Dispose();
-
- LayerMat = dst;
- }
- }
-
- public void Morph(OperationMorph operation, int iterations = 1, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (iterations == 0)
- iterations = (int) operation.IterationsStart;
-
- using (Mat dst = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(dst);
- CvInvoke.MorphologyEx(target, target, operation.MorphOperation, operation.Kernel.Matrix, operation.Kernel.Anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void MutateErode(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- if (ReferenceEquals(kernel, null))
- {
- kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- }
- using (Mat dst = LayerMat)
- {
- CvInvoke.Erode(dst, dst, kernel, anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void MutateDilate(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- if (ReferenceEquals(kernel, null))
- {
- kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- }
- using (Mat dst = LayerMat)
- {
- CvInvoke.Dilate(dst, dst, kernel, anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void MutateOpen(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- if (ReferenceEquals(kernel, null))
- {
- kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- }
- using (Mat dst = LayerMat)
- {
- CvInvoke.MorphologyEx(dst, dst, MorphOp.Open, kernel, anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void MutateClose(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- if (ReferenceEquals(kernel, null))
- {
- kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- }
- using (Mat dst = LayerMat)
- {
- CvInvoke.MorphologyEx(dst, dst, MorphOp.Close, kernel, anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void MutateGradient(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- if (ReferenceEquals(kernel, null))
- {
- kernel = CvInvoke.GetStructuringElement(ElementShape.Cross, new Size(3, 3), anchor);
- }
- using (Mat dst = LayerMat)
- {
- CvInvoke.MorphologyEx(dst, dst, MorphOp.Gradient, kernel, anchor, iterations, borderType, borderValue);
- LayerMat = dst;
- }
- }
-
- public void ThresholdPixels(OperationThreshold operation)
- {
- using (Mat dst = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(dst);
- CvInvoke.Threshold(target, target, operation.Threshold, operation.Maximum, operation.Type);
- LayerMat = dst;
- }
- }
-
- public void Blur(OperationBlur operation)
- {
- Size size = new Size((int) operation.Size, (int) operation.Size);
- Point anchor = operation.Kernel.Anchor;
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- //if (size.IsEmpty) size = new Size(3, 3);
- //if (anchor.IsEmpty) anchor = new Point(-1, -1);
- using (Mat dst = LayerMat)
- {
- Mat target = operation.GetRoiOrDefault(dst);
- switch (operation.BlurOperation)
- {
- case OperationBlur.BlurAlgorithm.Blur:
- CvInvoke.Blur(target, target, size, operation.Kernel.Anchor);
- break;
- case OperationBlur.BlurAlgorithm.Pyramid:
- CvInvoke.PyrDown(target, target);
- CvInvoke.PyrUp(target, target);
- break;
- case OperationBlur.BlurAlgorithm.MedianBlur:
- CvInvoke.MedianBlur(target, target, (int) operation.Size);
- break;
- case OperationBlur.BlurAlgorithm.GaussianBlur:
- CvInvoke.GaussianBlur(target, target, size, 0);
- break;
- case OperationBlur.BlurAlgorithm.Filter2D:
- CvInvoke.Filter2D(target, target, operation.Kernel.Matrix, anchor);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- LayerMat = dst;
- }
- }
-
- public void ChangeResolution(OperationChangeResolution operation)
- {
- using (var mat = LayerMat)
- {
- //mat.Transform(1, 1, newResolutionX-mat.Width+roi.Width, newResolutionY-mat.Height - roi.Height/2, new Size((int) newResolutionX, (int) newResolutionY));
- using (var matRoi = new Mat(mat, operation.VolumeBonds))
- {
- using (var matDst = new Mat(new Size((int)operation.NewResolutionX, (int)operation.NewResolutionY), mat.Depth,
- mat.NumberOfChannels))
- {
- using (var matDstRoi = new Mat(matDst,
- new Rectangle((int) (operation.NewResolutionX / 2 - operation.VolumeBonds.Width / 2),
- (int)operation.NewResolutionY / 2 - operation.VolumeBonds.Height / 2,
- operation.VolumeBonds.Width, operation.VolumeBonds.Height)))
- {
- matRoi.CopyTo(matDstRoi);
- LayerMat = matDst;
- }
- }
- }
- }
- }
-
- public void Pattern(OperationPattern operation)
- {
- using (var layer = LayerMat)
- using (var layerRoi = new Mat(layer, operation.ROI))
- using (var dstLayer = layer.CloneBlank())
- {
- for (ushort col = 0; col < operation.Cols; col++)
- for (ushort row = 0; row < operation.Rows; row++)
- {
- using (var dstRoi = new Mat(dstLayer, operation.GetRoi(col, row)))
- {
- layerRoi.CopyTo(dstRoi);
- }
- }
-
- LayerMat = dstLayer;
- }
- }
public Layer Clone()
{
- return new Layer(_index, CompressedBytes.ToArray(), ParentLayerManager)
+ return new(_index, CompressedBytes.ToArray(), ParentLayerManager)
{
PositionZ = _positionZ,
ExposureTime = _exposureTime,
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index 4ef3a93..f354ea2 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -28,7 +28,7 @@ namespace UVtools.Core
public class LayerManager : IEnumerable<Layer>
{
#region Properties
- public FileFormat SlicerFile { get; private set; }
+ public FileFormat SlicerFile { get; }
private Layer[] _layers;
@@ -38,9 +38,11 @@ namespace UVtools.Core
public Layer[] Layers
{
get => _layers;
- private set
+ protected internal set
{
_layers = value;
+ BoundingRectangle = Rectangle.Empty;
+ SlicerFile.LayerCount = Count;
if (value is null) return;
SlicerFile.PrintTime = SlicerFile.PrintTimeComputed;
}
@@ -59,7 +61,7 @@ namespace UVtools.Core
/// </summary>
public uint Count => (uint) Layers.Length;
- public byte LayerDigits => (byte) Layers.Length.ToString().Length;
+ public byte LayerDigits => (byte)Count.ToString().Length;
/// <summary>
/// Gets if any layer got modified, otherwise false
@@ -85,7 +87,7 @@ namespace UVtools.Core
public LayerManager(uint layerCount, FileFormat slicerFile)
{
SlicerFile = slicerFile;
- Layers = new Layer[layerCount];
+ _layers = new Layer[layerCount];
}
#endregion
@@ -201,7 +203,7 @@ namespace UVtools.Core
public Rectangle GetBoundingRectangle(OperationProgress progress = null)
{
- if (!_boundingRectangle.IsEmpty) return _boundingRectangle;
+ if (!_boundingRectangle.IsEmpty || Count == 0 || this[0] is null) return _boundingRectangle;
_boundingRectangle = this[0].BoundingRectangle;
if (_boundingRectangle.IsEmpty) // Safe checking
{
@@ -281,230 +283,6 @@ namespace UVtools.Core
return Layers[index];
}
- public void Move(OperationMove operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- if (operation.ROI == Rectangle.Empty) operation.ROI = GetBoundingRectangle(progress);
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- this[layerIndex].Move(operation);
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
- _boundingRectangle = Rectangle.Empty;
-
- progress.Token.ThrowIfCancellationRequested();
-
-
- }
-
- /// <summary>
- /// Resizes layer images in x and y factor, starting at 1 = 100%
- /// </summary>
- public void Resize(OperationResize operation, OperationProgress progress = null)
- {
- if (operation.X == 1m && operation.Y == 1m) return;
-
- if(ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- decimal xSteps = Math.Abs(operation.X - 1) / (operation.LayerIndexEnd - operation.LayerIndexStart);
- decimal ySteps = Math.Abs(operation.Y - 1) / (operation.LayerIndexEnd - operation.LayerIndexStart);
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var newX = operation.X;
- var newY = operation.Y;
- if (operation.IsFade)
- {
- if (newX != 1m)
- {
-
- //maxIteration = Math.Max(iterationsStart, iterationsEnd);
-
- newX = newX < 1m
- ? newX + (layerIndex - operation.LayerIndexStart) * xSteps
- : newX - (layerIndex - operation.LayerIndexStart) * xSteps;
-
- // constrain
- //iterations = Math.Min(Math.Max(1, iterations), maxIteration);
- }
-
- if (newY != 1m)
- {
-
- //maxIteration = Math.Max(iterationsStart, iterationsEnd);
-
- newY = (newY < 1m
- ? newY + (layerIndex - operation.LayerIndexStart) * ySteps
- : newY - (layerIndex - operation.LayerIndexStart) * ySteps);
-
- // constrain
- //iterations = Math.Min(Math.Max(1, iterations), maxIteration);
- }
- }
-
- lock (progress.Mutex)
- {
- progress++;
- }
-
- if (newX == 1.0m && newY == 1.0m) return;
-
- this[layerIndex].Resize((double) (newX / 100m), (double) (newY / 100m), operation);
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Flip(OperationFlip operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Flip(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Rotate(OperationRotate operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Rotate(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Solidify(OperationSolidify operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Solidify(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Mask(OperationMask operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Mask(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void PixelDimming(OperationPixelDimming operation, OperationProgress progress = null)
- {
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- if (operation.Pattern is null)
- {
- operation.Pattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 127, [0, 1] = 255,
- [1, 0] = 255, [1, 1] = 127,
- };
-
- if (operation.AlternatePattern is null)
- {
- operation.AlternatePattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 255, [0, 1] = 127,
- [1, 0] = 127, [1, 1] = 255,
- };
- }
- }
-
- if (operation.AlternatePattern is null)
- {
- operation.AlternatePattern = operation.Pattern;
- }
-
- using (Mat mat = this[0].LayerMat)
- using (Mat matPattern = mat.CloneBlank())
- using (Mat matAlternatePattern = mat.CloneBlank())
- {
- Mat target = operation.GetRoiOrDefault(mat);
-
- CvInvoke.Repeat(operation.Pattern, target.Rows / operation.Pattern.Rows + 1,
- target.Cols / operation.Pattern.Cols + 1, matPattern);
- CvInvoke.Repeat(operation.AlternatePattern, target.Rows / operation.AlternatePattern.Rows + 1,
- target.Cols / operation.AlternatePattern.Cols + 1, matAlternatePattern);
-
- using (var patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height)))
- using (var alternatePatternMask = new Mat(matAlternatePattern, new Rectangle(0, 0, target.Width, target.Height)))
- {
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].PixelDimming(operation, patternMask, alternatePatternMask);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- }
- }
-
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Infill(OperationInfill operation, OperationProgress progress)
- {
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Infill(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- }
-
public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration)
{
iterationSteps = 0;
@@ -535,379 +313,6 @@ namespace UVtools.Core
return MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, layerIndex);
}
- public void Morph(OperationMorph operation, BorderType borderType = BorderType.Default, MCvScalar borderValue = default, OperationProgress progress = null)
- {
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- var isFade = operation.Chamfer;
- MutateGetVarsIterationChamfer(
- operation.LayerIndexStart,
- operation.LayerIndexEnd,
- (int) operation.IterationsStart,
- (int) operation.IterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1,
- //new ParallelOptions {MaxDegreeOfParallelism = 1},
- layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, (int) operation.IterationsStart, (int) operation.IterationsEnd, iterationSteps, maxIteration, operation.LayerIndexStart, (uint)layerIndex);
- //Debug.WriteLine(iterations);
- this[layerIndex].Morph(operation, iterations, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- /*public void MutateErode(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, OperationProgress progress = null,
- IInputArray kernel = null, Point anchor = default,
- BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
-
- MutateGetVarsIterationFade(
- startLayerIndex,
- endLayerIndex,
- iterationsStart,
- iterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset("Eroding", endLayerIndex - startLayerIndex+1);
-
- Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint) layerIndex);
- this[layerIndex].MutateErode(iterations, kernel, anchor, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void MutateDilate(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, OperationProgress progress = null,
- IInputArray kernel = null, Point anchor = default,
- BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- MutateGetVarsIterationFade(
- startLayerIndex,
- endLayerIndex,
- iterationsStart,
- iterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset("Dilating", endLayerIndex - startLayerIndex+1);
-
- Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex);
- this[layerIndex].MutateDilate(iterations, kernel, anchor, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void MutateOpen(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, OperationProgress progress = null,
- IInputArray kernel = null, Point anchor = default,
- BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- MutateGetVarsIterationFade(
- startLayerIndex,
- endLayerIndex,
- iterationsStart,
- iterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset("Removing Noise", endLayerIndex - startLayerIndex+1);
-
- Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex);
- this[layerIndex].MutateOpen(iterations, kernel, anchor, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void MutateClose(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, OperationProgress progress = null,
- IInputArray kernel = null, Point anchor = default,
- BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- MutateGetVarsIterationFade(
- startLayerIndex,
- endLayerIndex,
- iterationsStart,
- iterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset("Gap Closing", endLayerIndex - startLayerIndex+1);
-
- Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex);
- this[layerIndex].MutateClose(iterations, kernel, anchor, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void MutateGradient(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, OperationProgress progress = null,
- IInputArray kernel = null, Point anchor = default,
- BorderType borderType = BorderType.Default, MCvScalar borderValue = default)
- {
- MutateGetVarsIterationFade(
- startLayerIndex,
- endLayerIndex,
- iterationsStart,
- iterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset("Gradient", endLayerIndex - startLayerIndex+1);
-
- Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex);
- this[layerIndex].MutateGradient(iterations, kernel, anchor, borderType, borderValue);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }*/
-
- public void RaftRelief(OperationRaftRelief operation, OperationProgress progress)
- {
- const uint minLength = 5;
- if (progress is null) progress = new OperationProgress();
- //progress.Reset(operation.ProgressAction);
-
- Mat supportsMat = null;
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
-
-
- uint firstSupportLayerIndex = 0;
- for (; firstSupportLayerIndex < Count; firstSupportLayerIndex++)
- {
- progress.Reset("Tracing raft", Count, firstSupportLayerIndex);
- if (progress.Token.IsCancellationRequested) return;
- supportsMat = operation.GetRoiOrDefault(this[firstSupportLayerIndex].LayerMat);
- var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 20, 100, 30, 5, 200);
- if (circles.Length >= minLength) break;
-
- supportsMat.Dispose();
- supportsMat = null;
- }
-
- if (supportsMat is null) return;
- Mat patternMat = null;
-
- if (operation.DilateIterations > 0)
- {
- CvInvoke.Dilate(supportsMat, supportsMat,
- CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(3, 3), new Point(-1, -1)),
- new Point(-1, -1), operation.DilateIterations, BorderType.Reflect101, new MCvScalar());
- }
-
- switch (operation.ReliefType)
- {
- case OperationRaftRelief.RaftReliefTypes.Relief:
- patternMat = EmguExtensions.InitMat(supportsMat.Size);
- int shapeSize = operation.HoleDiameter + operation.HoleSpacing;
- using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize)))
- {
-
- int center = operation.HoleDiameter / 2;
- //int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
- int radius = center;
- CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, EmguExtensions.WhiteByte, -1);
- CvInvoke.Circle(shape, new Point(0, 0), radius / 2, EmguExtensions.WhiteByte, -1);
- CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, EmguExtensions.WhiteByte, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, EmguExtensions.WhiteByte, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, EmguExtensions.WhiteByte, -1);
-
- CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
-
- patternMat = new Mat(patternMat, new Rectangle(0, 0, supportsMat.Width, supportsMat.Height));
- }
-
- break;
- }
-
- progress.Reset(operation.ProgressAction, firstSupportLayerIndex);
- Parallel.For(0, firstSupportLayerIndex, layerIndex =>
- {
- using (Mat dst = this[layerIndex].LayerMat)
- {
- var target = operation.GetRoiOrDefault(dst);
-
- switch (operation.ReliefType)
- {
- case OperationRaftRelief.RaftReliefTypes.Relief:
- using (Mat mask = new Mat())
- {
- /*CvInvoke.Subtract(target, supportsMat, mask);
- CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar());
- CvInvoke.Subtract(target, patternMat, target, mask);*/
-
- CvInvoke.Erode(target, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, default);
- CvInvoke.Subtract(mask, supportsMat, mask);
- CvInvoke.Subtract(target, patternMat, target, mask);
- }
-
- break;
- case OperationRaftRelief.RaftReliefTypes.Decimate:
- supportsMat.CopyTo(target);
- break;
- }
-
-
- this[layerIndex].LayerMat = dst;
- }
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
-
- supportsMat.Dispose();
- patternMat?.Dispose();
- }
-
- public void Arithmetic(OperationArithmetic operation, OperationProgress progress = null)
- {
- if (!operation.IsValid) return;
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, (uint)operation.Operations.Count);
-
- using (Mat result = this[operation.Operations[0].LayerIndex].LayerMat)
- {
- Mat resultRoi = operation.GetRoiOrDefault(result);
- for (int i = 1; i < operation.Operations.Count; i++)
- {
- using (var image = this[operation.Operations[i].LayerIndex].LayerMat)
- {
- Mat imageRoi = operation.GetRoiOrDefault(image);
- switch (operation.Operations[i - 1].Operator)
- {
- case OperationArithmetic.ArithmeticOperators.Add:
- CvInvoke.Add(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.Subtract:
- CvInvoke.Subtract(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.Multiply:
- CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.Divide:
- CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.BitwiseAnd:
- CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.BitwiseOr:
- CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi);
- break;
- case OperationArithmetic.ArithmeticOperators.BitwiseXor:
- CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi);
- break;
- }
- }
- }
-
- Parallel.ForEach(operation.SetLayers, layerIndex =>
- {
- if (operation.Operations.Count == 1 && operation.HaveROI)
- {
- var mat = this[layerIndex].LayerMat;
- var matRoi = operation.GetRoiOrDefault(mat);
- resultRoi.CopyTo(matRoi);
- this[layerIndex].LayerMat = mat;
- return;
- }
- this[layerIndex].LayerMat = result;
- });
- }
- }
-
- public void ThresholdPixels(OperationThreshold operation, OperationProgress progress)
- {
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].ThresholdPixels(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
-
- public void Blur(OperationBlur operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- this[layerIndex].Blur(operation);
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- progress.Token.ThrowIfCancellationRequested();
- }
-
public List<LayerIssue> GetAllIssues(
IslandDetectionConfiguration islandConfig = null,
OverhangDetectionConfiguration overhangConfig = null,
@@ -977,8 +382,8 @@ namespace UVtools.Core
(!ReferenceEquals(overhangConfig.WhiteListLayers, null) && !overhangConfig.WhiteListLayers.Contains(layer.Index)) &&
(!ReferenceEquals(islandConfig.WhiteListLayers, null) && !islandConfig.WhiteListLayers.Contains(layer.Index))
)
- )
)
+ )
{
lock (progress.Mutex)
{
@@ -1187,7 +592,7 @@ namespace UVtools.Core
// Check for overhangs
if (overhangConfig.Enabled && !overhangConfig.IndependentFromIslands && island is null
- || !ReferenceEquals(island, null) && islandConfig.EnhancedDetection && pixelsSupportingIsland >= 10
+ || !ReferenceEquals(island, null) && islandConfig.EnhancedDetection && pixelsSupportingIsland >= 10
)
{
points.Clear();
@@ -1278,7 +683,7 @@ namespace UVtools.Core
{
AddIssue(new LayerIssue(
layer, LayerIssue.IssueType.Overhang, vecPoints.ToArray(), layer.BoundingRectangle
- ));
+ ));
}
}
}
@@ -1559,495 +964,10 @@ namespace UVtools.Core
return result.OrderBy(issue => issue.Type).ThenBy(issue => issue.LayerIndex).ThenBy(issue => issue.PixelsCount).ToList();
}
- public void RepairLayers(OperationRepairLayers operation, OperationProgress progress = null)
- {
- if(ReferenceEquals(progress, null)) progress = new OperationProgress();
-
-
- var issues = operation.Issues;
-
- // Remove islands
- if (!ReferenceEquals(issues, null)
- && !ReferenceEquals(operation.IslandDetectionConfig, null)
- && operation.RepairIslands
- && operation.RemoveIslandsBelowEqualPixelCount > 0
- && operation.RemoveIslandsRecursiveIterations != 1)
- {
- progress.Reset("Removed recursive islands", 0);
- ushort limit = operation.RemoveIslandsRecursiveIterations == 0
- ? ushort.MaxValue
- : operation.RemoveIslandsRecursiveIterations;
-
- var recursiveIssues = issues;
- ConcurrentBag<uint> islandsToRecompute = null;
-
- var islandConfig = operation.IslandDetectionConfig;
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
- var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
- var emptyLayersConfig = false;
-
- islandConfig.Enabled = true;
- islandConfig.RequiredAreaToProcessCheck = (byte) Math.Ceiling(operation.RemoveIslandsBelowEqualPixelCount/2m);
-
- for (uint i = 0; i < limit; i++)
- {
- if (i > 0)
- {
- /*var whiteList = islandsToRecompute.GroupBy(u => u)
- .Select(grp => grp.First())
- .ToList();*/
- islandConfig.WhiteListLayers = islandsToRecompute.ToList();
- recursiveIssues = GetAllIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, emptyLayersConfig);
- //Debug.WriteLine(i);
- }
-
- var issuesGroup =
- recursiveIssues
- .Where(issue => issue.Type == LayerIssue.IssueType.Island &&
- issue.Pixels.Length <= operation.RemoveIslandsBelowEqualPixelCount)
- .GroupBy(issue => issue.LayerIndex);
-
- if (!issuesGroup.Any()) break; // Nothing to process
- islandsToRecompute = new ConcurrentBag<uint>();
- Parallel.ForEach(issuesGroup, group =>
- {
- if (progress.Token.IsCancellationRequested) return;
- Layer layer = this[group.Key];
- Mat image = layer.LayerMat;
- Span<byte> bytes = image.GetPixelSpan<byte>();
- foreach (var issue in group)
- {
- foreach (var issuePixel in issue.Pixels)
- {
- bytes[image.GetPixelPos(issuePixel)] = 0;
- }
-
- lock (progress.Mutex)
- {
- progress++;
- }
- }
-
- var nextLayerIndex = group.Key + 1;
- if(nextLayerIndex < Count)
- islandsToRecompute.Add(nextLayerIndex);
-
- layer.LayerMat = image;
- });
-
- if (islandsToRecompute.IsEmpty) break; // No more leftovers
- }
- }
-
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
- if (operation.RepairIslands || operation.RepairResinTraps)
- {
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- Layer layer = this[layerIndex];
- Mat image = null;
-
- void initImage()
- {
- if(image is null)
- image = layer.LayerMat;
- }
-
- if (!ReferenceEquals(issues, null))
- {
- if (operation.RepairIslands && operation.RemoveIslandsBelowEqualPixelCount > 0 && operation.RemoveIslandsRecursiveIterations == 1)
- {
- Span<byte> bytes = null;
- foreach (var issue in issues)
- {
- if (
- issue.LayerIndex != layerIndex ||
- issue.Type != LayerIssue.IssueType.Island ||
- issue.Pixels.Length > operation.RemoveIslandsBelowEqualPixelCount) continue;
-
- initImage();
- if(bytes == null)
- bytes = image.GetPixelSpan<byte>();
-
- foreach (var issuePixel in issue.Pixels)
- {
- bytes[image.GetPixelPos(issuePixel)] = 0;
- }
- }
- /*if (issues.TryGetValue((uint)layerIndex, out var issueList))
- {
- var bytes = image.GetPixelSpan<byte>();
- foreach (var issue in issueList.Where(issue =>
- issue.Type == LayerIssue.IssueType.Island && issue.Pixels.Length <= removeIslandsBelowEqualPixels))
- {
- foreach (var issuePixel in issue.Pixels)
- {
- bytes[image.GetPixelPos(issuePixel)] = 0;
- }
- }
- }*/
- }
-
- if (operation.RepairResinTraps)
- {
- foreach (var issue in issues.Where(issue => issue.LayerIndex == layerIndex && issue.Type == LayerIssue.IssueType.ResinTrap))
- {
- initImage();
- using (var vec = new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)))
- {
- CvInvoke.DrawContours(image,
- vec,
- -1,
- new MCvScalar(255),
- -1);
- }
- }
- }
- }
-
- if (operation.RepairIslands && (operation.GapClosingIterations > 0 || operation.NoiseRemovalIterations > 0))
- {
- initImage();
- using (Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3),
- new Point(-1, -1)))
- {
- if (operation.GapClosingIterations > 0)
- {
- CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1),
- (int)operation.GapClosingIterations, BorderType.Default, new MCvScalar());
- }
-
- if (operation.NoiseRemovalIterations > 0)
- {
- CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1),
- (int)operation.NoiseRemovalIterations, BorderType.Default, new MCvScalar());
- }
- }
- }
-
- if (!ReferenceEquals(image, null))
- {
- layer.LayerMat = image;
- image.Dispose();
- }
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
- }
-
- if (operation.RemoveEmptyLayers)
- {
- List<uint> removeLayers = new List<uint>();
- for (uint layerIndex = operation.LayerIndexStart; layerIndex <= operation.LayerIndexEnd; layerIndex++)
- {
- if (this[layerIndex].NonZeroPixelCount == 0)
- {
- removeLayers.Add(layerIndex);
- }
- }
-
- if (removeLayers.Count > 0)
- {
- RemoveLayers(removeLayers);
- }
- }
-
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void Import(OperationLayerImport operation, OperationProgress progress = null)
- {
- if (progress is null) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, (uint)operation.Count);
-
- var oldLayers = Layers;
- uint newLayerCount = operation.CalculateTotalLayers((uint) Layers.Length);
- uint startIndex = operation.LayerIndexStart;
- Layers = new Layer[newLayerCount];
-
- // Keep same layers up to InsertAfterLayerIndex
- for (uint i = 0; i <= operation.InsertAfterLayerIndex; i++)
- {
- Layers[i] = oldLayers[i];
- }
-
- // Keep all old layers if not discarding them
- if (operation.ReplaceSubsequentLayers)
- {
- if (!operation.DiscardRemainingLayers)
- {
- for (uint i = operation.InsertAfterLayerIndex + 1; i < oldLayers.Length; i++)
- {
- Layers[i] = oldLayers[i];
- }
- }
- }
- else // Push remaining layers to the end of imported layers
- {
- uint oldLayerIndex = operation.InsertAfterLayerIndex;
- for (uint i = operation.LayerIndexEnd + 1; i < newLayerCount; i++)
- {
- oldLayerIndex++;
- Layers[i] = oldLayers[oldLayerIndex];
- }
- }
-
-
- Parallel.For(0, operation.Count,
- //new ParallelOptions{MaxDegreeOfParallelism = 1},
- i =>
- {
- var mat = CvInvoke.Imread(operation.Files[i].TagString, ImreadModes.Grayscale);
- uint layerIndex = (uint) (startIndex + i);
- if (operation.MergeImages)
- {
- if (!(this[layerIndex] is null))
- {
- using (var oldMat = this[layerIndex].LayerMat)
- {
- CvInvoke.Add(oldMat, mat, mat);
- }
- }
- }
- this[layerIndex] = new Layer(layerIndex, mat, this);
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
- SlicerFile.LayerCount = Count;
- BoundingRectangle = Rectangle.Empty;
- SlicerFile.RequireFullEncode = true;
-
- RebuildLayersProperties();
-
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void CloneLayer(OperationLayerClone operation, OperationProgress progress = null)
- {
- var oldLayers = Layers;
-
- uint totalClones = (operation.LayerIndexEnd - operation.LayerIndexStart + 1) * operation.Clones;
- uint newLayerCount = Count + totalClones;
- Layers = new Layer[newLayerCount];
-
- progress.Reset(operation.ProgressAction, totalClones);
-
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
- {
- Layers[newLayerIndex] = oldLayers[layerIndex];
- if (layerIndex >= operation.LayerIndexStart && layerIndex <= operation.LayerIndexEnd)
- {
- for (uint i = 0; i < operation.Clones; i++)
- {
- newLayerIndex++;
- Layers[newLayerIndex] = oldLayers[layerIndex].Clone();
- Layers[newLayerIndex].IsModified = true;
-
- progress++;
- }
- }
-
- newLayerIndex++;
- }
-
- SlicerFile.LayerCount = Count;
- BoundingRectangle = Rectangle.Empty;
- SlicerFile.RequireFullEncode = true;
-
- RebuildLayersProperties();
-
- progress.Token.ThrowIfCancellationRequested();
- }
-
- public void RemoveLayer(uint layerIndex) => RemoveLayers(new OperationLayerRemove
- {
- LayerIndexStart = layerIndex,
- LayerIndexEnd = layerIndex,
- });
-
- public void RemoveLayers(OperationLayerRemove operation, OperationProgress progress = null)
- {
- if(progress is null)
- progress = new OperationProgress(false);
- var layersRemove = new List<uint>();
- for (uint layerIndex = operation.LayerIndexStart; layerIndex <= operation.LayerIndexEnd; layerIndex++)
- {
- layersRemove.Add(layerIndex);
- }
-
- RemoveLayers(layersRemove, progress);
- }
-
- public void RemoveLayers(List<uint> layersRemove, OperationProgress progress = null)
- {
- if (layersRemove.Count == 0) return;
-
- if (progress is null)
- progress = new OperationProgress(false);
-
- progress.Reset("removed layers", (uint) layersRemove.Count);
-
- var oldLayers = Layers;
- float layerHeight = SlicerFile.LayerHeight;
-
- Layers = new Layer[Count - layersRemove.Count];
-
- // Re-set
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
- {
- if (layersRemove.Contains(layerIndex)) continue;
- Layers[newLayerIndex] = oldLayers[layerIndex];
- Layers[newLayerIndex].Index = newLayerIndex;
-
- // Re-Z
- float posZ = layerHeight;
- if (newLayerIndex > 0)
- {
- if (oldLayers[layerIndex - 1].PositionZ == oldLayers[layerIndex].PositionZ)
- {
- posZ = Layers[newLayerIndex - 1].PositionZ;
- }
- else
- {
- posZ = (float)Math.Round(Layers[newLayerIndex - 1].PositionZ + layerHeight, 2);
- }
- }
-
- Layers[newLayerIndex].PositionZ = posZ;
- Layers[newLayerIndex].IsModified = true;
-
- newLayerIndex++;
- progress++;
- }
-
- SlicerFile.LayerCount = Count;
- BoundingRectangle = Rectangle.Empty;
- SlicerFile.RequireFullEncode = true;
- }
-
- public void ReHeight(OperationLayerReHeight operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.Item.LayerCount);
-
- var oldLayers = Layers;
-
- Layers = new Layer[operation.Item.LayerCount];
-
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
- {
- var oldLayer = oldLayers[layerIndex];
- if (operation.Item.IsDivision)
- {
- for (byte i = 0; i < operation.Item.Modifier; i++)
- {
- var newLayer = oldLayer.Clone();
- newLayer.Index = newLayerIndex;
- newLayer.PositionZ = (float) (operation.Item.LayerHeight * (newLayerIndex + 1));
- Layers[newLayerIndex] = newLayer;
- newLayerIndex++;
- progress++;
- }
- }
- else
- {
- using (var mat = oldLayers[layerIndex++].LayerMat)
- {
- for (byte i = 1; i < operation.Item.Modifier; i++)
- {
- using (var nextMat = oldLayers[layerIndex++].LayerMat)
- {
- CvInvoke.Add(mat, nextMat, mat);
- }
- }
-
- var newLayer = oldLayer.Clone();
- newLayer.Index = newLayerIndex;
- newLayer.PositionZ = (float) (operation.Item.LayerHeight * (newLayerIndex + 1));
- newLayer.LayerMat = mat;
- Layers[newLayerIndex] = newLayer;
- newLayerIndex++;
- layerIndex--;
- progress++;
- }
- }
- }
-
-
- SlicerFile.LayerHeight = (float)operation.Item.LayerHeight;
- SlicerFile.LayerCount = Count;
- BoundingRectangle = Rectangle.Empty;
- SlicerFile.RequireFullEncode = true;
- }
-
- public void ChangeResolution(OperationChangeResolution operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, Count);
-
- Parallel.For(0, Count, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- this[layerIndex].ChangeResolution(operation);
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
- progress.Token.ThrowIfCancellationRequested();
-
- SlicerFile.ResolutionX = operation.NewResolutionX;
- SlicerFile.ResolutionY = operation.NewResolutionY;
- }
-
- public void Pattern(OperationPattern operation, OperationProgress progress = null)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerRangeCount);
-
- Parallel.For(operation.LayerIndexStart, operation.LayerIndexEnd + 1, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- this[layerIndex].Pattern(operation);
-
- lock (progress.Mutex)
- {
- progress++;
- }
- });
-
- _boundingRectangle = Rectangle.Empty;
-
- progress.Token.ThrowIfCancellationRequested();
-
- if (operation.Anchor == Enumerations.Anchor.None) return;
- var operationMove = new OperationMove(BoundingRectangle, operation.ImageWidth, operation.ImageHeight, operation.Anchor)
- {
- LayerIndexStart = operation.LayerIndexStart, LayerIndexEnd = operation.LayerIndexEnd
- };
- Move(operationMove, progress);
-
- }
public void DrawModifications(IList<PixelOperation> drawings, OperationProgress progress = null)
{
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
+ progress ??= new OperationProgress();
progress.Reset("Drawings", (uint) drawings.Count);
ConcurrentDictionary<uint, Mat> modifiedLayers = new ConcurrentDictionary<uint, Mat>();
@@ -2261,219 +1181,5 @@ namespace UVtools.Core
#endregion
-
-
- public void CalibrateElephantFoot(OperationCalibrateElephantFoot operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, 3);
- SlicerFile.SuppressRebuildProperties = true;
-
- Layers = new Layer[operation.BottomLayers + operation.NormalLayers];
-
- SlicerFile.LayerHeight = (float) operation.LayerHeight;
- SlicerFile.BottomExposureTime = (float) operation.BottomExposure;
- SlicerFile.ExposureTime = (float) operation.NormalExposure;
- SlicerFile.BottomLayerCount = operation.BottomLayers;
-
- var layers = operation.GetLayers();
- progress++;
-
-
- var bottomLayer = new Layer(0, layers[0], this)
- {
- IsModified = true
- };
- var layer = new Layer(0, layers[1], this)
- {
- IsModified = true
- };
- bottomLayer.Move(new OperationMove(bottomLayer.BoundingRectangle, layers[0].Size));
- layer.Move(new OperationMove(layer.BoundingRectangle, layers[0].Size));
- progress++;
-
- for (uint layerIndex = 0;
- layerIndex < operation.BottomLayers+operation.NormalLayers;
- layerIndex++)
- {
- Layers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone());
- }
-
- foreach (var mat in layers)
- {
- mat.Dispose();
- }
-
- SlicerFile.LayerCount = Count;
- RebuildLayersProperties();
-
- BoundingRectangle = Rectangle.Empty;
-
- if(SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(operation.GetThumbnail());
-
- progress++;
-
- SlicerFile.SuppressRebuildProperties = false;
- }
-
- public void CalibrateXYZAccuracy(OperationCalibrateXYZAccuracy operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerCount);
-
- SlicerFile.SuppressRebuildProperties = true;
-
- Layers = new Layer[operation.LayerCount];
-
- SlicerFile.LayerHeight = (float)operation.LayerHeight;
- SlicerFile.BottomExposureTime = (float)operation.BottomExposure;
- SlicerFile.ExposureTime = (float)operation.NormalExposure;
- SlicerFile.BottomLayerCount = operation.BottomLayers;
-
- var layers = operation.GetLayers();
-
- var bottomLayer = new Layer(0, layers[0], this)
- {
- IsModified = true
- };
- var layer = new Layer(0, layers[1], this)
- {
- IsModified = true
- };
-
- for (uint layerIndex = 0; layerIndex < operation.LayerCount; layerIndex++)
- {
- Layers[layerIndex] = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone());
- progress++;
- }
-
- foreach (var mat in layers)
- {
- mat.Dispose();
- }
-
- SlicerFile.LayerCount = Count;
- RebuildLayersProperties();
-
- BoundingRectangle = Rectangle.Empty;
-
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(operation.GetThumbnail());
-
- SlicerFile.SuppressRebuildProperties = false;
- }
-
- public void CalibrateGrayscale(OperationCalibrateGrayscale operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerCount);
- SlicerFile.SuppressRebuildProperties = true;
-
- Layers = new Layer[operation.LayerCount];
-
- SlicerFile.LayerHeight = (float)operation.LayerHeight;
- SlicerFile.BottomExposureTime = (float)operation.BottomExposure;
- SlicerFile.ExposureTime = (float)operation.NormalExposure;
- SlicerFile.BottomLayerCount = operation.BottomLayers;
-
- var layers = operation.GetLayers();
- progress++;
-
-
- var bottomLayer = new Layer(0, layers[0], this)
- {
- IsModified = true
- };
- var interfaceLayer = operation.InterfaceLayers > 0 && layers[1] is not null ? new Layer(0, layers[1], this)
- {
- IsModified = true
- } : null;
- var layer = new Layer(0, layers[2], this)
- {
- IsModified = true
- };
-
- uint layerIndex = 0;
- for (uint i = 0; i < operation.BottomLayers; i++)
- {
- Layers[layerIndex] = bottomLayer.Clone();
- progress++;
- layerIndex++;
- }
-
- for (uint i = 0; i < operation.InterfaceLayers; i++)
- {
- Layers[layerIndex] = interfaceLayer.Clone();
- progress++;
- layerIndex++;
- }
-
-
- for (uint i = 0; i < operation.NormalLayers; i++)
- {
- Layers[layerIndex] = layer.Clone();
- progress++;
- layerIndex++;
- }
-
- foreach (var mat in layers)
- {
- mat?.Dispose();
- }
-
- SlicerFile.LayerCount = Count;
- RebuildLayersProperties();
-
- BoundingRectangle = Rectangle.Empty;
-
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(operation.GetThumbnail());
-
- progress++;
-
- SlicerFile.SuppressRebuildProperties = false;
- }
-
- public void CalibrateTolerance(OperationCalibrateTolerance operation, OperationProgress progress)
- {
- if (ReferenceEquals(progress, null)) progress = new OperationProgress();
- progress.Reset(operation.ProgressAction, operation.LayerCount);
- SlicerFile.SuppressRebuildProperties = true;
-
- Layers = new Layer[operation.LayerCount];
-
- SlicerFile.LayerHeight = (float)operation.LayerHeight;
- SlicerFile.BottomExposureTime = (float)operation.BottomExposure;
- SlicerFile.ExposureTime = (float)operation.NormalExposure;
- SlicerFile.BottomLayerCount = operation.BottomLayers;
-
- var layers = operation.GetLayers();
-
- Parallel.For(0, operation.LayerCount, layerIndex =>
- {
- Layers[layerIndex] = new Layer((uint) layerIndex, layers[layerIndex], this);
- layers[layerIndex].Dispose();
- lock (progress)
- {
- progress++;
- }
- });
-
- SlicerFile.LayerCount = Count;
- RebuildLayersProperties();
-
- BoundingRectangle = Rectangle.Empty;
- Move(new OperationMove(BoundingRectangle, SlicerFile.Resolution)
- {
- IsCutMove = true,
- LayerIndexEnd = Count-1
- }, progress);
-
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(operation.GetThumbnail());
-
- SlicerFile.SuppressRebuildProperties = false;
- }
}
}
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index 2786c0d..fabeb31 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -8,8 +8,10 @@
using System;
using System.Drawing;
+using System.Reflection.Metadata.Ecma335;
using System.Xml.Serialization;
using Emgu.CV;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -183,6 +185,16 @@ namespace UVtools.Core.Operations
}
}
+ public virtual bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual bool Execute(Mat mat, params object[] arguments)
+ {
+ throw new NotImplementedException();
+ }
+
public virtual void Dispose() { }
}
}
diff --git a/UVtools.Core/Operations/OperationArithmetic.cs b/UVtools.Core/Operations/OperationArithmetic.cs
index 00f9f61..fc6fe69 100644
--- a/UVtools.Core/Operations/OperationArithmetic.cs
+++ b/UVtools.Core/Operations/OperationArithmetic.cs
@@ -9,8 +9,10 @@
using System;
using System.Collections.Generic;
using System.Text;
+using System.Threading.Tasks;
using System.Xml.Serialization;
using Emgu.CV;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -18,8 +20,11 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationArithmetic : Operation
{
+ #region Members
private string _sentence;
+ #endregion
+ #region Enums
public enum ArithmeticOperators : byte
{
None,
@@ -31,7 +36,9 @@ namespace UVtools.Core.Operations
BitwiseOr,
BitwiseXor
}
+ #endregion
+ #region SubClasses
public sealed class ArithmeticOperation
{
public uint LayerIndex { get; }
@@ -43,7 +50,9 @@ namespace UVtools.Core.Operations
Operator = arithmeticOperator;
}
}
+ #endregion
+ #region Overrides
public override string Title => "Arithmetic";
public override string Description =>
"Perform arithmetic operations over the layers pixels.\n\n" +
@@ -81,19 +90,30 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ public override string ToString()
+ {
+ var result = $"{_sentence}" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Properties
public string Sentence
{
get => _sentence;
set => RaiseAndSetIfChanged(ref _sentence, value);
}
-
[XmlIgnore]
- public List<ArithmeticOperation> Operations { get; } = new List<ArithmeticOperation>();
+ public List<ArithmeticOperation> Operations { get; } = new();
[XmlIgnore]
public List<uint> SetLayers { get; } = new List<uint>();
public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0;
+ #endregion
+
+ #region Methods
public bool Parse()
{
@@ -185,13 +205,63 @@ namespace UVtools.Core.Operations
return true;
}
- public override string ToString()
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"{_sentence}" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ if (!IsValid) return false;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, (uint)Operations.Count);
+
+ using Mat result = slicerFile[Operations[0].LayerIndex].LayerMat;
+ Mat resultRoi = GetRoiOrDefault(result);
+ for (int i = 1; i < Operations.Count; i++)
+ {
+ using var image = slicerFile[Operations[i].LayerIndex].LayerMat;
+ Mat imageRoi = GetRoiOrDefault(image);
+ switch (Operations[i - 1].Operator)
+ {
+ case ArithmeticOperators.Add:
+ CvInvoke.Add(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.Subtract:
+ CvInvoke.Subtract(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.Multiply:
+ CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.Divide:
+ CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.BitwiseAnd:
+ CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.BitwiseOr:
+ CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi);
+ break;
+ case ArithmeticOperators.BitwiseXor:
+ CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi);
+ break;
+ }
+ }
+
+ Parallel.ForEach(SetLayers, layerIndex =>
+ {
+ if (Operations.Count == 1 && HaveROI)
+ {
+ var mat = slicerFile[layerIndex].LayerMat;
+ var matRoi = GetRoiOrDefault(mat);
+ resultRoi.CopyTo(matRoi);
+ slicerFile[layerIndex].LayerMat = mat;
+ return;
+ }
+ slicerFile[layerIndex].LayerMat = result;
+ });
+
+ return true;
}
+ #endregion
+
+ #region Equality
protected bool Equals(OperationArithmetic other)
{
return _sentence == other._sentence;
@@ -209,5 +279,6 @@ namespace UVtools.Core.Operations
{
return (_sentence != null ? _sentence.GetHashCode() : 0);
}
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs
index f8c1e58..a5237e6 100644
--- a/UVtools.Core/Operations/OperationBlur.cs
+++ b/UVtools.Core/Operations/OperationBlur.cs
@@ -7,8 +7,12 @@
*/
using System;
+using System.Drawing;
using System.Text;
+using System.Threading.Tasks;
using System.Xml.Serialization;
+using Emgu.CV;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -120,6 +124,66 @@ namespace UVtools.Core.Operations
#endregion
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = slicerFile[layerIndex].LayerMat)
+ {
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ }
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ Size size = new Size((int)Size, (int)Size);
+ Point anchor = Kernel.Anchor;
+ if (anchor.IsEmpty) anchor = new Point(-1, -1);
+ //if (size.IsEmpty) size = new Size(3, 3);
+ //if (anchor.IsEmpty) anchor = new Point(-1, -1);
+ Mat target = GetRoiOrDefault(mat);
+ switch (BlurOperation)
+ {
+ case BlurAlgorithm.Blur:
+ CvInvoke.Blur(target, target, size, Kernel.Anchor);
+ break;
+ case BlurAlgorithm.Pyramid:
+ CvInvoke.PyrDown(target, target);
+ CvInvoke.PyrUp(target, target);
+ break;
+ case BlurAlgorithm.MedianBlur:
+ CvInvoke.MedianBlur(target, target, (int)Size);
+ break;
+ case BlurAlgorithm.GaussianBlur:
+ CvInvoke.GaussianBlur(target, target, size, 0);
+ break;
+ case BlurAlgorithm.Filter2D:
+ CvInvoke.Filter2D(target, target, Kernel.Matrix, anchor);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return true;
+ }
+
+
+ #endregion
+
#region Equality
private bool Equals(OperationBlur other)
{
diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
index 3c477a6..49bcd2a 100644
--- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
+++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
@@ -16,6 +16,7 @@ using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -523,8 +524,7 @@ namespace UVtools.Core.Operations
new Rectangle(new Point(currentX, currentY), shape.Size)))
using (var erode = new Mat())
{
- CvInvoke.Erode(shape, erode, ErodeKernel.Matrix, ErodeKernel.Anchor, iteration, BorderType.Reflect101,
- default);
+ CvInvoke.Erode(shape, erode, ErodeKernel.Matrix, ErodeKernel.Anchor, iteration, BorderType.Reflect101, default);
erode.CopyTo(roi);
addText(roi, count, $"E: {iteration}i");
}
@@ -610,6 +610,65 @@ namespace UVtools.Core.Operations
return thumbnail;
}
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, 3);
+ slicerFile.SuppressRebuildProperties = true;
+
+ var newLayers = new Layer[LayerCount];
+
+ slicerFile.LayerHeight = (float)LayerHeight;
+ slicerFile.BottomExposureTime = (float)BottomExposure;
+ slicerFile.ExposureTime = (float)NormalExposure;
+ slicerFile.BottomLayerCount = BottomLayers;
+
+ var layers = GetLayers();
+ progress++;
+
+
+ var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+ var layer = new Layer(0, layers[1], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+ var moveOp = new OperationMove(bottomLayer.BoundingRectangle, layers[0].Size);
+ moveOp.Execute(layers[0]);
+ moveOp.Execute(layers[1]);
+
+ bottomLayer.LayerMat = layers[0];
+ layer.LayerMat = layers[1];
+
+ progress++;
+
+ for (uint layerIndex = 0;
+ layerIndex < LayerCount;
+ layerIndex++)
+ {
+ newLayers[layerIndex] = slicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone());
+ }
+
+ foreach (var mat in layers)
+ {
+ mat.Dispose();
+ }
+
+
+ if (slicerFile.ThumbnailsCount > 0)
+ slicerFile.SetThumbnails(GetThumbnail());
+
+ progress++;
+
+ slicerFile.LayerManager.Layers = newLayers;
+ slicerFile.SuppressRebuildProperties = false;
+ slicerFile.LayerManager.RebuildLayersProperties();
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
index 4e9a015..5ddddf7 100644
--- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
+++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
@@ -15,6 +15,7 @@ using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -437,6 +438,78 @@ namespace UVtools.Core.Operations
return thumbnail;
}
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerCount);
+ slicerFile.SuppressRebuildProperties = true;
+
+ var newLayers = new Layer[LayerCount];
+
+ slicerFile.LayerHeight = (float)LayerHeight;
+ slicerFile.BottomExposureTime = (float)BottomExposure;
+ slicerFile.ExposureTime = (float)NormalExposure;
+ slicerFile.BottomLayerCount = BottomLayers;
+
+ var layers = GetLayers();
+ progress++;
+
+
+ var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+ var interfaceLayer = InterfaceLayers > 0 && layers[1] is not null ? new Layer(0, layers[1], slicerFile.LayerManager)
+ {
+ IsModified = true
+ } : null;
+ var layer = new Layer(0, layers[2], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+
+ uint layerIndex = 0;
+ for (uint i = 0; i < BottomLayers; i++)
+ {
+ newLayers[layerIndex] = bottomLayer.Clone();
+ progress++;
+ layerIndex++;
+ }
+
+ for (uint i = 0; i < InterfaceLayers; i++)
+ {
+ newLayers[layerIndex] = interfaceLayer.Clone();
+ progress++;
+ layerIndex++;
+ }
+
+
+ for (uint i = 0; i < NormalLayers; i++)
+ {
+ newLayers[layerIndex] = layer.Clone();
+ progress++;
+ layerIndex++;
+ }
+
+ foreach (var mat in layers)
+ {
+ mat?.Dispose();
+ }
+
+
+ if (slicerFile.ThumbnailsCount > 0)
+ slicerFile.SetThumbnails(GetThumbnail());
+
+ progress++;
+
+
+ slicerFile.LayerManager.Layers = newLayers;
+ slicerFile.SuppressRebuildProperties = false;
+ slicerFile.LayerManager.RebuildLayersProperties();
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
index 82b3dfe..a7fc0ff 100644
--- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs
+++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
@@ -16,6 +16,7 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -474,8 +475,6 @@ namespace UVtools.Core.Operations
#endregion
#region Methods
-
-
public Mat[] GetLayers()
{
var layers = new Mat[LayerCount];
@@ -695,6 +694,49 @@ namespace UVtools.Core.Operations
return thumbnail;
}
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerCount);
+ slicerFile.SuppressRebuildProperties = true;
+
+ var newLayers = new Layer[LayerCount];
+
+ slicerFile.LayerHeight = (float)LayerHeight;
+ slicerFile.BottomExposureTime = (float)BottomExposure;
+ slicerFile.ExposureTime = (float)NormalExposure;
+ slicerFile.BottomLayerCount = BottomLayers;
+
+ var layers = GetLayers();
+
+ Parallel.For(0, LayerCount, layerIndex =>
+ {
+ newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], slicerFile.LayerManager);
+ layers[layerIndex].Dispose();
+ lock (progress)
+ {
+ progress++;
+ }
+ });
+
+ slicerFile.LayerManager.Layers = newLayers;
+ slicerFile.LayerManager.RebuildLayersProperties();
+
+ var moveOp = new OperationMove(slicerFile.LayerManager.BoundingRectangle, slicerFile.Resolution)
+ {
+ IsCutMove = true,
+ LayerIndexEnd = slicerFile.LayerCount - 1
+ };
+ moveOp.Execute(slicerFile, progress);
+
+ if (slicerFile.ThumbnailsCount > 0)
+ slicerFile.SetThumbnails(GetThumbnail());
+
+ slicerFile.SuppressRebuildProperties = false;
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
index df0fe59..7ac8790 100644
--- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
+++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
@@ -14,6 +14,7 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -703,6 +704,51 @@ namespace UVtools.Core.Operations
return thumbnail;
}
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerCount);
+
+ slicerFile.SuppressRebuildProperties = true;
+
+ var newLayers = new Layer[LayerCount];
+
+ slicerFile.LayerHeight = (float)LayerHeight;
+ slicerFile.BottomExposureTime = (float)BottomExposure;
+ slicerFile.ExposureTime = (float)NormalExposure;
+ slicerFile.BottomLayerCount = BottomLayers;
+
+ var layers = GetLayers();
+
+ var bottomLayer = new Layer(0, layers[0], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+ var layer = new Layer(0, layers[1], slicerFile.LayerManager)
+ {
+ IsModified = true
+ };
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ newLayers[layerIndex] = slicerFile.GetInitialLayerValueOrNormal(layerIndex, bottomLayer.Clone(), layer.Clone());
+ progress++;
+ }
+
+ foreach (var mat in layers)
+ {
+ mat.Dispose();
+ }
+
+ if (slicerFile.ThumbnailsCount > 0)
+ slicerFile.SetThumbnails(GetThumbnail());
+
+ slicerFile.LayerManager.Layers = newLayers;
+ slicerFile.LayerManager.RebuildLayersProperties();
+ slicerFile.SuppressRebuildProperties = false;
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs
index da9160c..fe4d3cf 100644
--- a/UVtools.Core/Operations/OperationChangeResolution.cs
+++ b/UVtools.Core/Operations/OperationChangeResolution.cs
@@ -9,6 +9,9 @@
using System;
using System.Drawing;
using System.Text;
+using System.Threading.Tasks;
+using Emgu.CV;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -16,8 +19,10 @@ namespace UVtools.Core.Operations
[Serializable]
public sealed class OperationChangeResolution : Operation
{
+ #region Members
private uint _newResolutionX;
private uint _newResolutionY;
+ #endregion
#region Subclasses
public class Resolution
@@ -84,6 +89,13 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ public override string ToString()
+ {
+ var result = $"{_newResolutionX} x {_newResolutionY}";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
#endregion
#region Properties
@@ -146,13 +158,55 @@ namespace UVtools.Core.Operations
public static Resolution[] Presets => GetResolutions();
- public override string ToString()
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"{_newResolutionX} x {_newResolutionY}";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, slicerFile.LayerCount);
+
+ Parallel.For(0, slicerFile.LayerCount, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = slicerFile[layerIndex].LayerMat;
+ using var matRoi = new Mat(mat, VolumeBonds);
+ using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
+ using var matDstRoi = new Mat(matDst,
+ new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2),
+ (int)NewResolutionY / 2 - VolumeBonds.Height / 2,
+ VolumeBonds.Width, VolumeBonds.Height));
+ matRoi.CopyTo(matDstRoi);
+ //Execute(mat);
+ slicerFile[layerIndex].LayerMat = matDst;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ slicerFile.ResolutionX = NewResolutionX;
+ slicerFile.ResolutionY = NewResolutionY;
+
+ return true;
}
+ /*public override bool Execute(Mat mat, params object[] arguments)
+ {
+ //mat.Transform(1, 1, newResolutionX-mat.Width+roi.Width, newResolutionY-mat.Height - roi.Height/2, new Size((int) newResolutionX, (int) newResolutionY));
+ using var matRoi = new Mat(mat, VolumeBonds);
+ using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
+ using var matDstRoi = new Mat(matDst,
+ new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2),
+ (int)NewResolutionY / 2 - VolumeBonds.Height / 2,
+ VolumeBonds.Width, VolumeBonds.Height));
+ matRoi.CopyTo(matDstRoi);
+
+ return true;
+ }*/
+
#endregion
#region Equality
diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs
index 7f774a6..b31e017 100644
--- a/UVtools.Core/Operations/OperationEditParameters.cs
+++ b/UVtools.Core/Operations/OperationEditParameters.cs
@@ -17,8 +17,11 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationEditParameters : Operation
{
+ #region Members
private bool _perLayerOverride;
+ #endregion
+ #region Overrides
public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI => false;
@@ -75,6 +78,9 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ #endregion
+
+ #region Propertiers
public FileFormat.PrintParameterModifier[] Modifiers { get; set; }
@@ -86,6 +92,7 @@ namespace UVtools.Core.Operations
get => _perLayerOverride;
set => RaiseAndSetIfChanged(ref _perLayerOverride, value);
}
+ #endregion
public OperationEditParameters()
{
@@ -95,5 +102,15 @@ namespace UVtools.Core.Operations
{
Modifiers = modifiers;
}
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ slicerFile.EditPrintParameters(this);
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs
index 7a63814..5381d1b 100644
--- a/UVtools.Core/Operations/OperationFlip.cs
+++ b/UVtools.Core/Operations/OperationFlip.cs
@@ -7,16 +7,22 @@
*/
using System;
+using System.Threading.Tasks;
+using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationFlip : Operation
{
+ #region Members
private bool _makeCopy;
private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.Horizontally;
+ #endregion
+ #region Overrides
public override string Title => "Flip";
public override string Description =>
"Flip the layers of the model vertically and/or horizontally.\n" +
@@ -34,6 +40,16 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Flipped layers";
+ public override string ToString()
+ {
+ var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Properties
+
public Enumerations.FlipDirection FlipDirection
{
get => _flipDirection;
@@ -69,13 +85,7 @@ namespace UVtools.Core.Operations
return flipType;
}
}
-
- public override string ToString()
- {
- var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ #endregion
#region Equality
@@ -101,5 +111,45 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ #region Methods
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ var target = GetRoiOrDefault(mat);
+
+ if (MakeCopy)
+ {
+ using Mat dst = new Mat();
+ CvInvoke.Flip(target, dst, FlipTypeOpenCV);
+ CvInvoke.Add(target, dst, target);
+ }
+ else
+ {
+ CvInvoke.Flip(target, target, FlipTypeOpenCV);
+ }
+
+ return true;
+ }
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs
index e305338..b05891e 100644
--- a/UVtools.Core/Operations/OperationInfill.cs
+++ b/UVtools.Core/Operations/OperationInfill.cs
@@ -7,17 +7,26 @@
*/
using System;
+using System.Drawing;
+using System.Threading.Tasks;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public sealed class OperationInfill : Operation
{
+ #region Members
private InfillAlgorithm _infillType = InfillAlgorithm.CubicDynamicLink;
private ushort _wallThickness = 64;
private ushort _infillThickness = 45;
private ushort _infillSpacing = 160;
private ushort _infillBrightness = 255;
+ #endregion
#region Overrides
@@ -89,7 +98,200 @@ namespace UVtools.Core.Operations
#endregion
#region Equality
-
+
+ #endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat, layerIndex);
+ slicerFile[layerIndex].LayerMat = mat;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ if (arguments is null || arguments.Length < 1) return false;
+ var anchor = new Point(-1, -1);
+ var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
+ uint index = Convert.ToUInt32(arguments[0]);
+ uint layerIndex = index - LayerIndexStart;
+ var infillColor = new MCvScalar(InfillBrightness);
+
+ Mat patternMask = null;
+ using Mat erode = new Mat();
+ using Mat diff = new Mat();
+ Mat target = GetRoiOrDefault(mat);
+
+
+ if (InfillType == InfillAlgorithm.Cubic ||
+ InfillType == InfillAlgorithm.CubicCenterLink ||
+ InfillType == InfillAlgorithm.CubicDynamicLink ||
+ InfillType == InfillAlgorithm.CubicInterlinked)
+ {
+ using var infillPattern = EmguExtensions.InitMat(new Size(InfillSpacing, InfillSpacing));
+ using Mat matPattern = mat.CloneBlank();
+ bool firstPattern = true;
+ uint accumulator = 0;
+ uint step = 0;
+ bool dynamicCenter = false;
+ while (accumulator < layerIndex)
+ {
+ dynamicCenter = !dynamicCenter;
+ firstPattern = true;
+ accumulator += InfillSpacing;
+
+ if (accumulator >= layerIndex) break;
+ firstPattern = false;
+ accumulator += InfillThickness;
+ }
+
+ if (firstPattern)
+ {
+ int thickness = InfillThickness / 2;
+ // Top Left
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, 0, thickness, thickness),
+ infillColor, -1);
+
+ // Top Right
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(infillPattern.Width - thickness, 0, thickness, thickness),
+ infillColor, -1);
+
+ // Bottom Left
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, infillPattern.Height - thickness, thickness, thickness),
+ infillColor, -1);
+
+ // Bottom Right
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(infillPattern.Width - thickness, infillPattern.Height - thickness,
+ thickness, thickness),
+ infillColor, -1);
+
+ // Center cross
+ int margin = (int) (InfillSpacing - accumulator + layerIndex) - thickness;
+ int marginInv = (int) (accumulator - layerIndex) - thickness;
+
+ if (InfillType == InfillAlgorithm.CubicCenterLink ||
+ (InfillType == InfillAlgorithm.CubicDynamicLink &&
+ dynamicCenter) ||
+ InfillType == InfillAlgorithm.CubicInterlinked)
+ {
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, margin, InfillThickness, InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, marginInv, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, marginInv, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, margin, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+ }
+
+
+ if (InfillType == InfillAlgorithm.CubicInterlinked ||
+ (InfillType == InfillAlgorithm.CubicDynamicLink &&
+ !dynamicCenter))
+ {
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, -thickness, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, -thickness, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(-thickness, margin, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(-thickness, marginInv, InfillThickness,
+ InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(InfillSpacing - thickness, margin,
+ InfillThickness, InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(InfillSpacing - thickness, marginInv,
+ InfillThickness, InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, InfillSpacing - thickness,
+ InfillThickness, InfillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, InfillSpacing - thickness,
+ InfillThickness, InfillThickness),
+ infillColor, -1);
+ }
+
+
+ }
+ else
+ {
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, 0, InfillSpacing, InfillSpacing),
+ infillColor, InfillThickness);
+ }
+
+
+ {
+ CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
+ target.Cols / infillPattern.Cols + 1, matPattern);
+ patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
+ }
+ }
+
+
+ CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101,
+ default);
+ CvInvoke.Subtract(target, erode, diff);
+
+
+ CvInvoke.BitwiseAnd(erode, patternMask, target);
+ CvInvoke.Add(target, diff, target);
+ patternMask?.Dispose();
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs
index eb77b6c..211cb7d 100644
--- a/UVtools.Core/Operations/OperationLayerClone.cs
+++ b/UVtools.Core/Operations/OperationLayerClone.cs
@@ -7,7 +7,9 @@
*/
using System;
+using System.Drawing;
using System.Text;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -15,7 +17,9 @@ namespace UVtools.Core.Operations
[Serializable]
public sealed class OperationLayerClone : Operation
{
+ #region Members
private uint _clones = 1;
+ #endregion
#region Overrides
@@ -50,6 +54,13 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ public override string ToString()
+ {
+ var result = $"[Clones: {Clones}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
#endregion
#region Properties
@@ -65,11 +76,45 @@ namespace UVtools.Core.Operations
#endregion
- public override string ToString()
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"[Clones: {Clones}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ var oldLayers = slicerFile.LayerManager.Layers;
+
+ uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones;
+ uint newLayerCount = (uint) (oldLayers.Length + totalClones);
+ var layers = new Layer[newLayerCount];
+
+ progress.Reset(ProgressAction, totalClones);
+
+ uint newLayerIndex = 0;
+ for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ {
+ layers[newLayerIndex] = oldLayers[layerIndex];
+ if (layerIndex >= LayerIndexStart && layerIndex <= LayerIndexEnd)
+ {
+ for (uint i = 0; i < Clones; i++)
+ {
+ newLayerIndex++;
+ layers[newLayerIndex] = oldLayers[layerIndex].Clone();
+ layers[newLayerIndex].IsModified = true;
+
+ progress++;
+ }
+ }
+
+ newLayerIndex++;
+ }
+
+ slicerFile.LayerManager.Layers = layers;
+ slicerFile.RequireFullEncode = true;
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
}
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 4a22279..17c4dba 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -7,7 +7,6 @@
*/
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
@@ -16,6 +15,7 @@ using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -185,6 +185,74 @@ namespace UVtools.Core.Operations
return result;
}
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, (uint)Count);
+
+ var oldLayers = slicerFile.LayerManager.Layers;
+ uint newLayerCount = CalculateTotalLayers((uint)oldLayers.Length);
+ uint startIndex = LayerIndexStart;
+ var layers = new Layer[newLayerCount];
+
+ // Keep same layers up to InsertAfterLayerIndex
+ for (uint i = 0; i <= InsertAfterLayerIndex; i++)
+ {
+ layers[i] = oldLayers[i];
+ }
+
+ // Keep all old layers if not discarding them
+ if (ReplaceSubsequentLayers)
+ {
+ if (!DiscardRemainingLayers)
+ {
+ for (uint i = InsertAfterLayerIndex + 1; i < oldLayers.Length; i++)
+ {
+ layers[i] = oldLayers[i];
+ }
+ }
+ }
+ else // Push remaining layers to the end of imported layers
+ {
+ uint oldLayerIndex = InsertAfterLayerIndex;
+ for (uint i = LayerIndexEnd + 1; i < newLayerCount; i++)
+ {
+ oldLayerIndex++;
+ layers[i] = oldLayers[oldLayerIndex];
+ }
+ }
+
+
+ Parallel.For(0, Count,
+ //new ParallelOptions{MaxDegreeOfParallelism = 1},
+ i =>
+ {
+ var mat = CvInvoke.Imread(Files[i].TagString, ImreadModes.Grayscale);
+ uint layerIndex = (uint)(startIndex + i);
+ if (MergeImages)
+ {
+ if (layers[layerIndex] is not null)
+ {
+ using var oldMat = layers[layerIndex].LayerMat;
+ CvInvoke.Add(oldMat, mat, mat);
+ }
+ }
+ layers[layerIndex] = new Layer(layerIndex, mat, slicerFile.LayerManager);
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ slicerFile.LayerManager.Layers = layers;
+ slicerFile.RequireFullEncode = true;
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs
index aa9dea2..fb04a34 100644
--- a/UVtools.Core/Operations/OperationLayerReHeight.cs
+++ b/UVtools.Core/Operations/OperationLayerReHeight.cs
@@ -8,9 +8,10 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Text;
+using Emgu.CV;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -18,7 +19,9 @@ namespace UVtools.Core.Operations
[Serializable]
public sealed class OperationLayerReHeight : Operation
{
+ #region Members
private OperationLayerReHeightItem _item;
+ #endregion
#region Overrides
@@ -38,8 +41,6 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Height adjusted layers";
- public override bool CanCancel => false;
-
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters)
@@ -55,8 +56,15 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ public override string ToString()
+ {
+ var result = $"[Layer Count: {Item.LayerCount}] [Layer Height: {Item.LayerHeight}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
#endregion
+ #region Properties
public OperationLayerReHeightItem Item
{
get => _item;
@@ -89,8 +97,9 @@ namespace UVtools.Core.Operations
return list.ToArray();
}
+ #endregion
-
+ #region Subclasses
public class OperationLayerReHeightItem
{
public bool IsMultiply { get; }
@@ -112,12 +121,62 @@ namespace UVtools.Core.Operations
return (IsMultiply ? 'x' : '÷') + $" {Modifier} → {LayerCount} layers at {LayerHeight}mm";
}
}
+ #endregion
- public override string ToString()
+ #region Methods
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"[Layer Count: {Item.LayerCount}] [Layer Height: {Item.LayerHeight}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, Item.LayerCount);
+
+ var oldLayers = slicerFile.LayerManager.Layers;
+
+ var layers = new Layer[Item.LayerCount];
+
+ uint newLayerIndex = 0;
+ for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+
+ var oldLayer = oldLayers[layerIndex];
+ if (Item.IsDivision)
+ {
+ for (byte i = 0; i < Item.Modifier; i++)
+ {
+ var newLayer = oldLayer.Clone();
+ newLayer.Index = newLayerIndex;
+ newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1));
+ layers[newLayerIndex] = newLayer;
+ newLayerIndex++;
+ progress++;
+ }
+ }
+ else
+ {
+ using var mat = oldLayers[layerIndex++].LayerMat;
+ for (byte i = 1; i < Item.Modifier; i++)
+ {
+ using var nextMat = oldLayers[layerIndex++].LayerMat;
+ CvInvoke.Add(mat, nextMat, mat);
+ }
+
+ var newLayer = oldLayer.Clone();
+ newLayer.Index = newLayerIndex;
+ newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1));
+ newLayer.LayerMat = mat;
+ layers[newLayerIndex] = newLayer;
+ newLayerIndex++;
+ layerIndex--;
+ progress++;
+ }
+ }
+
+ slicerFile.LayerManager.Layers = layers;
+ slicerFile.LayerHeight = (float)Item.LayerHeight;
+ slicerFile.RequireFullEncode = true;
+
+ return true;
}
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs
index 25dd621..45816dd 100644
--- a/UVtools.Core/Operations/OperationLayerRemove.cs
+++ b/UVtools.Core/Operations/OperationLayerRemove.cs
@@ -7,7 +7,10 @@
*/
using System;
+using System.Collections.Generic;
using System.Text;
+using Emgu.CV;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
@@ -43,5 +46,68 @@ namespace UVtools.Core.Operations
#endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress(false);
+ var layersRemove = new List<uint>();
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
+ {
+ layersRemove.Add(layerIndex);
+ }
+
+ return RemoveLayers(slicerFile, layersRemove, progress);
+ }
+
+ public static bool RemoveLayers(FileFormat slicerFile, List<uint> layersRemove, OperationProgress progress = null)
+ {
+ if (layersRemove.Count == 0) return false;
+
+ progress ??= new OperationProgress(false);
+
+ progress.Reset("removed layers", (uint)layersRemove.Count);
+
+ var oldLayers = slicerFile.LayerManager.Layers;
+ float layerHeight = slicerFile.LayerHeight;
+
+ var layers = new Layer[oldLayers.Length - layersRemove.Count];
+
+ // Re-set
+ uint newLayerIndex = 0;
+ for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ {
+ if (layersRemove.Contains(layerIndex)) continue;
+ layers[newLayerIndex] = oldLayers[layerIndex];
+ layers[newLayerIndex].Index = newLayerIndex;
+
+ // Re-Z
+ float posZ = layerHeight;
+ if (newLayerIndex > 0)
+ {
+ if (oldLayers[layerIndex - 1].PositionZ == oldLayers[layerIndex].PositionZ)
+ {
+ posZ = layers[newLayerIndex - 1].PositionZ;
+ }
+ else
+ {
+ posZ = (float)Math.Round(layers[newLayerIndex - 1].PositionZ + layerHeight, 2);
+ }
+ }
+
+ layers[newLayerIndex].PositionZ = posZ;
+ layers[newLayerIndex].IsModified = true;
+
+ newLayerIndex++;
+ progress++;
+ }
+
+ slicerFile.LayerManager.Layers = layers;
+ slicerFile.RequireFullEncode = true;
+
+ return true;
+ }
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index c702371..972157f 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -8,9 +8,11 @@
using System;
using System.Text;
+using System.Threading.Tasks;
using System.Xml.Serialization;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -18,6 +20,7 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationMask : Operation
{
+ #region Overrides
public override string Title => "Mask";
public override string Description =>
"Mask the intensity of the LCD output using a greyscale input image.\n\n" +
@@ -45,16 +48,52 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ #endregion
+ #region Properties
[XmlIgnore]
public Mat Mask { get; set; }
public bool HaveMask => !(Mask is null);
+ #endregion
+
+ #region Methods
public void InvertMask()
{
if (!HaveMask) return;
CvInvoke.BitwiseNot(Mask, Mask);
}
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ var target = GetRoiOrDefault(mat);
+ if (Mask.Size != target.Size) return false;
+ CvInvoke.BitwiseAnd(target, Mask, target);
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs
index ab603d5..bdf5e8a 100644
--- a/UVtools.Core/Operations/OperationMorph.cs
+++ b/UVtools.Core/Operations/OperationMorph.cs
@@ -7,8 +7,11 @@
*/
using System;
+using System.Threading.Tasks;
using System.Xml.Serialization;
+using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -16,11 +19,13 @@ namespace UVtools.Core.Operations
[Serializable]
public sealed class OperationMorph : Operation
{
+ #region Members
private MorphOp _morphOperation = MorphOp.Erode;
private uint _iterationsStart = 1;
private uint _iterationsEnd = 1;
private bool _chamfer;
-
+ #endregion
+
#region Overrides
public override string Title => "Morph";
@@ -123,5 +128,57 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ var isFade = Chamfer;
+ LayerManager.MutateGetVarsIterationChamfer(
+ LayerIndexStart,
+ LayerIndexEnd,
+ (int)IterationsStart,
+ (int)IterationsEnd,
+ ref isFade,
+ out var iterationSteps,
+ out var maxIteration
+ );
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1,
+ //new ParallelOptions {MaxDegreeOfParallelism = 1},
+ layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ int iterations = LayerManager.MutateGetIterationVar(isFade, (int)IterationsStart, (int)IterationsEnd, iterationSteps, maxIteration, LayerIndexStart, (uint)layerIndex);
+
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat, iterations);
+ slicerFile[layerIndex].LayerMat = mat;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ int iterations = (int) _iterationsStart;
+ if (arguments is not null && arguments.Length >= 1)
+ {
+ iterations = (int) arguments[0];
+ }
+ var target = GetRoiOrDefault(mat);
+ CvInvoke.MorphologyEx(target, target, MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default);
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs
index d3141e7..09f046d 100644
--- a/UVtools.Core/Operations/OperationMove.cs
+++ b/UVtools.Core/Operations/OperationMove.cs
@@ -6,9 +6,12 @@
* of this license document, but changing it is not allowed.
*/
using System;
-using System.Diagnostics;
using System.Drawing;
using System.Text;
+using System.Threading.Tasks;
+using Emgu.CV;
+using Emgu.CV.Structure;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -16,6 +19,7 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationMove : Operation
{
+ #region Overrides
public override string Title => "Move";
public override string Description =>
"Change or copy the position of the model on the build plate.\n" +
@@ -45,6 +49,15 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ public override string ToString()
+ {
+ var result = $"[{ROI} -> {DstRoi}] [Cut: {IsCutMove}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Members
private Rectangle _dstRoi = Rectangle.Empty;
private uint _imageWidth;
private uint _imageHeight;
@@ -55,6 +68,9 @@ namespace UVtools.Core.Operations
private int _marginBottom;
private bool _isCutMove = true;
private bool _isWithinBoundary;
+ #endregion
+
+ #region Properties
public Rectangle DstRoi
{
@@ -66,59 +82,6 @@ namespace UVtools.Core.Operations
}
}
- public void CalculateDstRoi()
- {
- _dstRoi.Size = ROI.Size;
-
- switch (Anchor)
- {
- case Enumerations.Anchor.TopLeft:
- _dstRoi.Location = new Point(0, 0);
- break;
- case Enumerations.Anchor.TopCenter:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), 0);
- break;
- case Enumerations.Anchor.TopRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), 0);
- break;
- case Enumerations.Anchor.MiddleLeft:
- _dstRoi.Location = new Point(0, (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.MiddleCenter:
- //case Anchor.None:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.MiddleRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.BottomLeft:
- _dstRoi.Location = new Point(0, (int)(ImageHeight - ROI.Height));
- break;
- case Enumerations.Anchor.BottomCenter:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight - ROI.Height));
- break;
- case Enumerations.Anchor.BottomRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight - ROI.Height));
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- _dstRoi.X += MarginLeft;
- _dstRoi.X -= MarginRight;
- _dstRoi.Y += MarginTop;
- _dstRoi.Y -= MarginBottom;
-
- IsWithinBoundary = !(DstRoi.IsEmpty || DstRoi.X < 0 || DstRoi.Y < 0 ||
- DstRoi.Width == 0 || DstRoi.Right > ImageWidth ||
- DstRoi.Height == 0 || DstRoi.Bottom > ImageHeight);
-
- RaisePropertyChanged(nameof(DstRoi));
- RaisePropertyChanged(nameof(LocationXStr));
- RaisePropertyChanged(nameof(LocationYStr));
- }
-
-
public uint ImageWidth
{
get => _imageWidth;
@@ -205,6 +168,61 @@ namespace UVtools.Core.Operations
public string IsWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
+ #endregion
+
+ #region Methods
+ public void CalculateDstRoi()
+ {
+ _dstRoi.Size = ROI.Size;
+
+ switch (Anchor)
+ {
+ case Enumerations.Anchor.TopLeft:
+ _dstRoi.Location = new Point(0, 0);
+ break;
+ case Enumerations.Anchor.TopCenter:
+ _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), 0);
+ break;
+ case Enumerations.Anchor.TopRight:
+ _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), 0);
+ break;
+ case Enumerations.Anchor.MiddleLeft:
+ _dstRoi.Location = new Point(0, (int)(ImageHeight / 2 - ROI.Height / 2));
+ break;
+ case Enumerations.Anchor.MiddleCenter:
+ //case Anchor.None:
+ _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight / 2 - ROI.Height / 2));
+ break;
+ case Enumerations.Anchor.MiddleRight:
+ _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight / 2 - ROI.Height / 2));
+ break;
+ case Enumerations.Anchor.BottomLeft:
+ _dstRoi.Location = new Point(0, (int)(ImageHeight - ROI.Height));
+ break;
+ case Enumerations.Anchor.BottomCenter:
+ _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight - ROI.Height));
+ break;
+ case Enumerations.Anchor.BottomRight:
+ _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight - ROI.Height));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ _dstRoi.X += MarginLeft;
+ _dstRoi.X -= MarginRight;
+ _dstRoi.Y += MarginTop;
+ _dstRoi.Y -= MarginBottom;
+
+ IsWithinBoundary = !(DstRoi.IsEmpty || DstRoi.X < 0 || DstRoi.Y < 0 ||
+ DstRoi.Width == 0 || DstRoi.Right > ImageWidth ||
+ DstRoi.Height == 0 || DstRoi.Bottom > ImageHeight);
+
+ RaisePropertyChanged(nameof(DstRoi));
+ RaisePropertyChanged(nameof(LocationXStr));
+ RaisePropertyChanged(nameof(LocationYStr));
+ }
+
public OperationMove()
{
}
@@ -220,8 +238,8 @@ namespace UVtools.Core.Operations
public OperationMove(Rectangle srcRoi, Size resolution, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter)
{
ROI = srcRoi;
- ImageWidth = (uint) resolution.Width;
- ImageHeight = (uint) resolution.Height;
+ ImageWidth = (uint)resolution.Width;
+ ImageHeight = (uint)resolution.Height;
Anchor = anchor;
}
@@ -239,18 +257,64 @@ namespace UVtools.Core.Operations
}
-
public bool ValidateBounds()
{
CalculateDstRoi();
return IsWithinBoundary;
}
- public override string ToString()
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"[{ROI} -> {DstRoi}] [Cut: {IsCutMove}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ if (ROI == Rectangle.Empty) ROI = slicerFile.LayerManager.GetBoundingRectangle(progress);
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using (var mat = slicerFile[layerIndex].LayerMat)
+ {
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ slicerFile.LayerManager.BoundingRectangle = Rectangle.Empty;
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
}
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+
+ if (ImageWidth == 0) ImageWidth = (uint) mat.Width;
+ if (ImageHeight == 0) ImageHeight = (uint) mat.Height;
+
+ using var srcRoi = new Mat(mat, ROI);
+ using var dstRoi = new Mat(mat, DstRoi);
+ if (IsCutMove)
+ {
+ using var targetRoi = srcRoi.Clone();
+ srcRoi.SetTo(new MCvScalar(0));
+ targetRoi.CopyTo(dstRoi);
+ }
+ else
+ {
+ srcRoi.CopyTo(dstRoi);
+ }
+
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index 592aa18..97d75f7 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -9,6 +9,10 @@
using System;
using System.Drawing;
using System.Text;
+using System.Threading.Tasks;
+using Emgu.CV;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -16,6 +20,7 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationPattern : Operation
{
+ #region Members
private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
private ushort _colSpacing;
private ushort _rowSpacing;
@@ -26,6 +31,7 @@ namespace UVtools.Core.Operations
private ushort _maxCols;
private ushort _maxRows;
private bool _isWithinBoundary = true;
+ #endregion
#region Overrides
@@ -62,6 +68,7 @@ namespace UVtools.Core.Operations
#endregion
+ #region Properties
public Enumerations.Anchor Anchor
{
get => _anchor;
@@ -176,7 +183,8 @@ namespace UVtools.Core.Operations
public string InfoModelWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
- public Size GetPatternVolume => new Size(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing);
+ public Size GetPatternVolume => new(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing);
+ #endregion
public OperationPattern()
{
@@ -191,6 +199,7 @@ namespace UVtools.Core.Operations
Fill();
}
+ #region Methods
public void SetAnchor(byte value)
{
Anchor = (Enumerations.Anchor)value;
@@ -262,5 +271,64 @@ namespace UVtools.Core.Operations
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = slicerFile[layerIndex].LayerMat;
+ using var layerRoi = new Mat(mat, ROI);
+ using var dstLayer = mat.CloneBlank();
+ for (ushort col = 0; col < Cols; col++)
+ for (ushort row = 0; row < Rows; row++)
+ {
+ var roi = GetRoi(col, row);
+ using var dstRoi = new Mat(dstLayer, roi);
+ layerRoi.CopyTo(dstRoi);
+ }
+ //Execute(mat);
+ slicerFile[layerIndex].LayerMat = dstLayer;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ slicerFile.LayerManager.BoundingRectangle = Rectangle.Empty;
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ if (Anchor == Enumerations.Anchor.None) return true;
+ var operationMove = new OperationMove(slicerFile.LayerManager.BoundingRectangle, ImageWidth, ImageHeight, Anchor)
+ {
+ LayerIndexStart = LayerIndexStart,
+ LayerIndexEnd = LayerIndexEnd
+ };
+ operationMove.Execute(slicerFile, progress);
+
+ return true;
+ }
+
+ /*public override bool Execute(Mat mat, params object[] arguments)
+ {
+ using var layerRoi = new Mat(mat, ROI);
+ using var dstLayer = mat.CloneBlank();
+ for (ushort col = 0; col < Cols; col++)
+ for (ushort row = 0; row < Rows; row++)
+ {
+ var dstRoi = new Mat(dstLayer, GetRoi(col, row));
+ layerRoi.CopyTo(dstRoi);
+ }
+
+ return true;
+ }*/
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index e14a3ba..25c16e8 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -7,10 +7,14 @@
*/
using System;
+using System.Drawing;
using System.Text;
+using System.Threading.Tasks;
using System.Xml.Serialization;
using Emgu.CV;
+using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -30,6 +34,8 @@ namespace UVtools.Core.Operations
}
}
#endregion
+
+ #region Members
private uint _wallThicknessStart = 5;
private uint _wallThicknessEnd = 5;
private bool _wallsOnly;
@@ -42,6 +48,7 @@ namespace UVtools.Core.Operations
private byte _brightness = 127;
private ushort _infillGenThickness = 10;
private ushort _infillGenSpacing = 20;
+ #endregion
#region Overrides
public override string Title => "Pixel dimming";
@@ -526,6 +533,103 @@ namespace UVtools.Core.Operations
return;
}
}
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ if (Pattern is null)
+ {
+ Pattern = new Matrix<byte>(2, 2)
+ {
+ [0, 0] = 127,
+ [0, 1] = 255,
+ [1, 0] = 255,
+ [1, 1] = 127,
+ };
+
+ AlternatePattern ??= new Matrix<byte>(2, 2)
+ {
+ [0, 0] = 255,
+ [0, 1] = 127,
+ [1, 0] = 127,
+ [1, 1] = 255,
+ };
+ }
+
+ AlternatePattern ??= Pattern;
+
+ using var blankMat = EmguExtensions.InitMat(slicerFile.Resolution);
+ using var matPattern = blankMat.CloneBlank();
+ using var matAlternatePattern = blankMat.CloneBlank();
+ var target = GetRoiOrDefault(blankMat);
+
+ CvInvoke.Repeat(Pattern, target.Rows / Pattern.Rows + 1, target.Cols / Pattern.Cols + 1, matPattern);
+ CvInvoke.Repeat(AlternatePattern, target.Rows / AlternatePattern.Rows + 1, target.Cols / AlternatePattern.Cols + 1, matAlternatePattern);
+
+ using var patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
+ using var alternatePatternMask = new Mat(matAlternatePattern, new Rectangle(0, 0, target.Width, target.Height));
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat, layerIndex, patternMask, alternatePatternMask);
+ slicerFile[layerIndex].LayerMat = mat;
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ if (arguments is null || arguments.Length < 2) return false;
+ var anchor = new Point(-1, -1);
+ var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
+
+ uint layerIndex = Convert.ToUInt32(arguments[0]);
+ Mat patternMask = (Mat)arguments[1];
+ Mat alternatePatternMask = arguments.Length >= 3 && arguments[2] is not null ? (Mat)arguments[2] : patternMask;
+
+ int wallThickness = LayerManager.MutateGetIterationChamfer(
+ layerIndex,
+ LayerIndexStart,
+ LayerIndexEnd,
+ (int)WallThicknessStart,
+ (int)WallThicknessEnd,
+ Chamfer
+ );
+
+
+ using Mat erode = new Mat();
+ using Mat diff = new Mat();
+ Mat target = GetRoiOrDefault(mat);
+
+
+ CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
+ CvInvoke.Subtract(target, erode, diff);
+
+
+ if (WallsOnly)
+ {
+ CvInvoke.BitwiseAnd(diff, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target);
+ CvInvoke.Add(erode, target, target);
+ }
+ else
+ {
+ CvInvoke.BitwiseAnd(erode, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target);
+ CvInvoke.Add(target, diff, target);
+ }
+
+ return true;
+ }
+
#endregion
}
}
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index 4c5a90a..c76b55a 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -7,17 +7,39 @@
*/
using System;
+using System.Drawing;
+using System.Threading.Tasks;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationRaftRelief : Operation
{
+ #region Enums
+ public enum RaftReliefTypes : byte
+ {
+ Relief,
+ Dimming,
+ Decimate
+ }
+ #endregion
+
+ #region Members
private RaftReliefTypes _reliefType = RaftReliefTypes.Relief;
+ private byte _ignoreFirstLayers;
+ private byte _brightness;
private byte _dilateIterations = 10;
private byte _wallMargin = 20;
private byte _holeDiameter = 50;
private byte _holeSpacing = 20;
+ #endregion
+
+ #region Overrides
public override string Title => "Raft relief";
public override string Description =>
"Relief raft by adding holes in between to reduce FEP suction, save resin and easier to remove the prints.";
@@ -32,33 +54,52 @@ namespace UVtools.Core.Operations
public override Enumerations.LayerRangeSelection StartLayerRangeSelection =>
Enumerations.LayerRangeSelection.None;
-
-
- public OperationRaftRelief()
- {
- }
-
+
public override string ToString()
{
- var result = $"[{_reliefType}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
+ var result = $"[{_reliefType}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
+ #endregion
- public enum RaftReliefTypes : byte
- {
- Relief,
- Decimate
- }
-
+ #region Properties
public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes));
public RaftReliefTypes ReliefType
{
get => _reliefType;
- set => RaiseAndSetIfChanged(ref _reliefType, value);
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _reliefType, value)) return;
+ RaisePropertyChanged(nameof(IsRelief));
+ RaisePropertyChanged(nameof(IsDimming));
+ RaisePropertyChanged(nameof(IsDecimate));
+ }
+ }
+
+ public bool IsRelief => _reliefType == RaftReliefTypes.Relief;
+ public bool IsDimming => _reliefType == RaftReliefTypes.Dimming;
+ public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate;
+
+ public byte IgnoreFirstLayers
+ {
+ get => _ignoreFirstLayers;
+ set => RaiseAndSetIfChanged(ref _ignoreFirstLayers, value);
+ }
+
+ public byte Brightness
+ {
+ get => _brightness;
+ set
+ {
+ if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
+ }
}
+ public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
+
public byte DilateIterations
{
get => _dilateIterations;
@@ -82,12 +123,13 @@ namespace UVtools.Core.Operations
get => _holeSpacing;
set => RaiseAndSetIfChanged(ref _holeSpacing, value);
}
+ #endregion
#region Equality
protected bool Equals(OperationRaftRelief other)
{
- return _reliefType == other._reliefType && _dilateIterations == other._dilateIterations && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing && _wallMargin == other._wallMargin;
+ return _reliefType == other._reliefType && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing;
}
public override bool Equals(object obj)
@@ -100,15 +142,120 @@ namespace UVtools.Core.Operations
public override int GetHashCode()
{
- unchecked
+ return HashCode.Combine((int) _reliefType, _ignoreFirstLayers, _brightness, _dilateIterations, _wallMargin, _holeDiameter, _holeSpacing);
+ }
+
+ #endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ const uint minLength = 5;
+ progress ??= new OperationProgress();
+ //progress.Reset(operation.ProgressAction);
+
+ Mat supportsMat = null;
+ var anchor = new Point(-1, -1);
+ var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
+
+
+ uint firstSupportLayerIndex = 0;
+ for (; firstSupportLayerIndex < slicerFile.LayerCount; firstSupportLayerIndex++)
{
- var hashCode = (int) _reliefType;
- hashCode = (hashCode * 397) ^ _dilateIterations.GetHashCode();
- hashCode = (hashCode * 397) ^ _holeDiameter.GetHashCode();
- hashCode = (hashCode * 397) ^ _holeSpacing.GetHashCode();
- hashCode = (hashCode * 397) ^ _wallMargin.GetHashCode();
- return hashCode;
+ progress.Reset("Tracing raft", slicerFile.LayerCount, firstSupportLayerIndex);
+ if (progress.Token.IsCancellationRequested) return false;
+ supportsMat = GetRoiOrDefault(slicerFile[firstSupportLayerIndex].LayerMat);
+ var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 20, 100, 30, 5, 200);
+ if (circles.Length >= minLength) break;
+
+ supportsMat.Dispose();
+ supportsMat = null;
+ }
+
+ if (supportsMat is null) return false;
+ Mat patternMat = null;
+
+ if (DilateIterations > 0)
+ {
+ CvInvoke.Dilate(supportsMat, supportsMat,
+ CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(3, 3), new Point(-1, -1)),
+ new Point(-1, -1), DilateIterations, BorderType.Reflect101, new MCvScalar());
}
+
+ var color = new MCvScalar(255 - Brightness);
+
+ switch (ReliefType)
+ {
+ case OperationRaftRelief.RaftReliefTypes.Relief:
+ patternMat = EmguExtensions.InitMat(supportsMat.Size);
+ int shapeSize = HoleDiameter + HoleSpacing;
+ using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize)))
+ {
+
+ int center = HoleDiameter / 2;
+ //int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
+ int radius = center;
+ CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
+ CvInvoke.Circle(shape, new Point(0, 0), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, color, -1);
+
+ CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
+
+ patternMat = new Mat(patternMat, new Rectangle(0, 0, supportsMat.Width, supportsMat.Height));
+ }
+
+ break;
+ case OperationRaftRelief.RaftReliefTypes.Dimming:
+ patternMat = EmguExtensions.InitMat(supportsMat.Size, color);
+ break;
+ }
+
+ progress.Reset(ProgressAction, firstSupportLayerIndex);
+ Parallel.For(IgnoreFirstLayers, firstSupportLayerIndex, layerIndex =>
+ {
+ using (Mat dst = slicerFile[layerIndex].LayerMat)
+ {
+ var target = GetRoiOrDefault(dst);
+
+ switch (ReliefType)
+ {
+ case RaftReliefTypes.Relief:
+ case RaftReliefTypes.Dimming:
+ using (Mat mask = new Mat())
+ {
+ /*CvInvoke.Subtract(target, supportsMat, mask);
+ CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar());
+ CvInvoke.Subtract(target, patternMat, target, mask);*/
+
+ CvInvoke.Erode(target, mask, kernel, anchor, WallMargin, BorderType.Reflect101, default);
+ CvInvoke.Subtract(mask, supportsMat, mask);
+ CvInvoke.Subtract(target, patternMat, target, mask);
+ }
+
+ break;
+ case RaftReliefTypes.Decimate:
+ supportsMat.CopyTo(target);
+ break;
+ }
+
+
+ slicerFile[layerIndex].LayerMat = dst;
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+
+ supportsMat.Dispose();
+ patternMat?.Dispose();
+
+ return true;
}
#endregion
diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs
new file mode 100644
index 0000000..7f156b1
--- /dev/null
+++ b/UVtools.Core/Operations/OperationRedrawModel.cs
@@ -0,0 +1,249 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
+using UVtools.Core.Objects;
+
+namespace UVtools.Core.Operations
+{
+ [Serializable]
+ public class OperationRedrawModel : Operation
+ {
+ #region Members
+
+ private string _filePath;
+ private byte _brightness = 220;
+ private bool _contactPointsOnly = true;
+ private RedrawTypes _redrawType = RedrawTypes.Supports;
+ private bool _ignoreContactLessPixels = true;
+
+ #endregion
+
+ #region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection { get; } = Enumerations.LayerRangeSelection.None;
+
+ public override string Title => "Redraw model/supports";
+
+ public override string Description =>
+ "Redraw the model or supports with a set brightness. This requires an extra sliced file from same object but without any supports and raft, straight to the build plate.\n" +
+ "Note: Run this tool prior to any made modification. You must find the optimal exposure/brightness combo, or supports can fail.";
+
+ public override string ConfirmationText => "redraw the "+ (_redrawType == RedrawTypes.Supports ? "supports" : "model") +
+ $" with an brightness of {_brightness}?";
+
+ public override string ProgressTitle => "Redrawing " + (_redrawType == RedrawTypes.Supports ? "supports" : "model");
+
+ public override string ProgressAction => "Redraw layers";
+
+ public override StringTag Validate(params object[] parameters)
+ {
+ var sb = new StringBuilder();
+
+ if (IsFileValid() is null)
+ {
+ sb.AppendLine("The selected file is not valid.");
+ }
+
+ return new StringTag(sb.ToString());
+ }
+
+
+ public override string ToString()
+ {
+ var result = $"[{_redrawType}] [B: {_brightness}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+
+ #endregion
+
+ #region Enums
+ public enum RedrawTypes : byte
+ {
+ Supports,
+ Model,
+ }
+ #endregion
+
+ #region Properties
+
+ [XmlIgnore]
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
+
+ public RedrawTypes RedrawType
+ {
+ get => _redrawType;
+ set => RaiseAndSetIfChanged(ref _redrawType, value);
+ }
+
+ public static Array RedrawTypesItems => Enum.GetValues(typeof(RedrawTypes));
+
+ public byte Brightness
+ {
+ get => _brightness;
+ set
+ {
+ if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
+ }
+ }
+
+ public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
+
+ public bool ContactPointsOnly
+ {
+ get => _contactPointsOnly;
+ set => RaiseAndSetIfChanged(ref _contactPointsOnly, value);
+ }
+
+ public bool IgnoreContactLessPixels
+ {
+ get => _ignoreContactLessPixels;
+ set => RaiseAndSetIfChanged(ref _ignoreContactLessPixels, value);
+ }
+
+ #endregion
+
+ #region Equality
+
+ protected bool Equals(OperationRedrawModel other)
+ {
+ return _brightness == other._brightness && _contactPointsOnly == other._contactPointsOnly && _redrawType == other._redrawType && _ignoreContactLessPixels == other._ignoreContactLessPixels;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationRedrawModel) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_brightness, _contactPointsOnly, (int) _redrawType, _ignoreContactLessPixels);
+ }
+
+ #endregion
+
+ #region Methods
+
+ public FileFormat IsFileValid(bool returnNewInstance = false) =>
+ FileFormat.FindByExtension(_filePath, true, returnNewInstance);
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+
+ var otherFile = IsFileValid(true);
+ otherFile.Decode(_filePath, progress);
+
+ progress.Reset(ProgressAction, otherFile.LayerCount);
+
+ int startLayerIndex = (int)(slicerFile.LayerCount - otherFile.LayerCount);
+ if (startLayerIndex < 0) return false;
+ Parallel.For(0, otherFile.LayerCount, layerIndex =>
+ {
+ var fullMatLayerIndex = startLayerIndex + layerIndex;
+ using var fullMat = slicerFile[fullMatLayerIndex].LayerMat;
+ using var bodyMat = otherFile[layerIndex].LayerMat;
+ using var fullMatRoi = GetRoiOrDefault(fullMat);
+ using var bodyMatRoi = GetRoiOrDefault(bodyMat);
+ using var patternMat = EmguExtensions.InitMat(fullMatRoi.Size, new MCvScalar(255 - _brightness));
+ using var supportsMat = new Mat();
+
+ bool modified = false;
+ if (_redrawType == RedrawTypes.Supports && _contactPointsOnly)
+ {
+ if (layerIndex + 1 >= otherFile.LayerCount) return;
+ CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
+ using var contours = new VectorOfVectorOfPoint();
+ using var hierarchyMat = new Mat();
+ CvInvoke.FindContours(supportsMat, contours, hierarchyMat, RetrType.List, ChainApproxMethod.ChainApproxSimple);
+ if (contours.Size <= 0) return;
+ using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
+ using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
+ var fullSpan = fullMatRoi.GetPixelSpan<byte>();
+ var supportsSpan = supportsMat.GetPixelSpan<byte>();
+ var nextSpan = nextLayerMatRoi.GetPixelSpan<byte>();
+ for (int i = 0; i < contours.Size; i++)
+ {
+ var foundContour = false;
+ var rectangle = CvInvoke.BoundingRectangle(contours[i]);
+ for (int y = rectangle.Y; y < rectangle.Bottom && !foundContour; y++)
+ for (int x = rectangle.X; x < rectangle.Right; x++)
+ {
+ var pos = supportsMat.GetPixelPos(x, y);
+ if (_ignoreContactLessPixels)
+ {
+ if (supportsSpan[pos] <= 10) continue;
+ if (nextSpan[pos] <= 0) continue;
+ modified = true;
+ fullSpan[pos] = _brightness;
+ }
+ else
+ {
+ if (supportsSpan[pos] <= 100) continue;
+ if (nextSpan[pos] <= 150) continue;
+ CvInvoke.DrawContours(fullMatRoi, contours, i, new MCvScalar(_brightness), -1, LineType.AntiAlias);
+ modified = true;
+ foundContour = true;
+ break;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ switch (_redrawType)
+ {
+ case RedrawTypes.Supports:
+ CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
+ break;
+ case RedrawTypes.Model:
+ CvInvoke.BitwiseAnd(fullMatRoi, bodyMatRoi, supportsMat); // Model
+ break;
+ }
+
+ CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat);
+ modified = true;
+ }
+
+ if (modified)
+ {
+ slicerFile[fullMatLayerIndex].LayerMat = fullMat;
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index 2622432..e46d3c2 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -7,9 +7,19 @@
*/
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using System.Xml.Serialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -17,13 +27,17 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationRepairLayers : Operation
{
+ #region Members
private bool _repairIslands = true;
private bool _repairResinTraps = true;
private bool _removeEmptyLayers = true;
private byte _removeIslandsBelowEqualPixelCount = 5;
private ushort _removeIslandsRecursiveIterations = 4;
private uint _gapClosingIterations = 1;
- private uint _noiseRemovalIterations = 0;
+ private uint _noiseRemovalIterations;
+ #endregion
+
+ #region Overrides
public override bool CanROI => false;
public override string Title => "Repair layers and issues";
public override string Description => null;
@@ -48,7 +62,9 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ #endregion
+ #region Properties
public bool RepairIslands
{
get => _repairIslands;
@@ -96,5 +112,207 @@ namespace UVtools.Core.Operations
[XmlIgnore]
public List<LayerIssue> Issues { get; set; }
+ #endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+
+
+
+ // Remove islands
+ if (!ReferenceEquals(Issues, null)
+ && !ReferenceEquals(IslandDetectionConfig, null)
+ && RepairIslands
+ && RemoveIslandsBelowEqualPixelCount > 0
+ && RemoveIslandsRecursiveIterations != 1)
+ {
+ progress.Reset("Removed recursive islands", 0);
+ ushort limit = RemoveIslandsRecursiveIterations == 0
+ ? ushort.MaxValue
+ : RemoveIslandsRecursiveIterations;
+
+ var recursiveIssues = Issues;
+ ConcurrentBag<uint> islandsToRecompute = null;
+
+ var islandConfig = IslandDetectionConfig;
+ var overhangConfig = new OverhangDetectionConfiguration(false);
+ var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
+ var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
+ var emptyLayersConfig = false;
+
+ islandConfig.Enabled = true;
+ islandConfig.RequiredAreaToProcessCheck = (byte)Math.Ceiling(RemoveIslandsBelowEqualPixelCount / 2m);
+
+ for (uint i = 0; i < limit; i++)
+ {
+ if (i > 0)
+ {
+ /*var whiteList = islandsToRecompute.GroupBy(u => u)
+ .Select(grp => grp.First())
+ .ToList();*/
+ islandConfig.WhiteListLayers = islandsToRecompute.ToList();
+ recursiveIssues = slicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, emptyLayersConfig);
+ //Debug.WriteLine(i);
+ }
+
+ var issuesGroup =
+ recursiveIssues
+ .Where(issue => issue.Type == LayerIssue.IssueType.Island &&
+ issue.Pixels.Length <= RemoveIslandsBelowEqualPixelCount)
+ .GroupBy(issue => issue.LayerIndex);
+
+ if (!issuesGroup.Any()) break; // Nothing to process
+ islandsToRecompute = new ConcurrentBag<uint>();
+ Parallel.ForEach(issuesGroup, group =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ Layer layer = slicerFile[group.Key];
+ Mat image = layer.LayerMat;
+ Span<byte> bytes = image.GetPixelSpan<byte>();
+ foreach (var issue in group)
+ {
+ foreach (var issuePixel in issue.Pixels)
+ {
+ bytes[image.GetPixelPos(issuePixel)] = 0;
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ }
+
+ var nextLayerIndex = group.Key + 1;
+ if (nextLayerIndex < slicerFile.LayerCount)
+ islandsToRecompute.Add(nextLayerIndex);
+
+ layer.LayerMat = image;
+ });
+
+ if (islandsToRecompute.IsEmpty) break; // No more leftovers
+ }
+ }
+
+ progress.Reset(ProgressAction, LayerRangeCount);
+ if (RepairIslands || RepairResinTraps)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ Layer layer = slicerFile[layerIndex];
+ Mat image = null;
+
+ void initImage()
+ {
+ image ??= layer.LayerMat;
+ }
+
+ if (Issues is not null)
+ {
+ if (RepairIslands && RemoveIslandsBelowEqualPixelCount > 0 && RemoveIslandsRecursiveIterations == 1)
+ {
+ Span<byte> bytes = null;
+ foreach (var issue in Issues)
+ {
+ if (
+ issue.LayerIndex != layerIndex ||
+ issue.Type != LayerIssue.IssueType.Island ||
+ issue.Pixels.Length > RemoveIslandsBelowEqualPixelCount) continue;
+
+ initImage();
+ if (bytes == null)
+ bytes = image.GetPixelSpan<byte>();
+
+ foreach (var issuePixel in issue.Pixels)
+ {
+ bytes[image.GetPixelPos(issuePixel)] = 0;
+ }
+ }
+ /*if (issues.TryGetValue((uint)layerIndex, out var issueList))
+ {
+ var bytes = image.GetPixelSpan<byte>();
+ foreach (var issue in issueList.Where(issue =>
+ issue.Type == LayerIssue.IssueType.Island && issue.Pixels.Length <= removeIslandsBelowEqualPixels))
+ {
+ foreach (var issuePixel in issue.Pixels)
+ {
+ bytes[image.GetPixelPos(issuePixel)] = 0;
+ }
+ }
+ }*/
+ }
+
+ if (RepairResinTraps)
+ {
+ foreach (var issue in Issues.Where(issue => issue.LayerIndex == layerIndex && issue.Type == LayerIssue.IssueType.ResinTrap))
+ {
+ initImage();
+ using var vec = new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels));
+ CvInvoke.DrawContours(image,
+ vec,
+ -1,
+ EmguExtensions.WhiteByte,
+ -1);
+ }
+ }
+ }
+
+ if (RepairIslands && (GapClosingIterations > 0 || NoiseRemovalIterations > 0))
+ {
+ initImage();
+ using Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3),
+ new Point(-1, -1));
+ if (GapClosingIterations > 0)
+ {
+ CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1),
+ (int)GapClosingIterations, BorderType.Default, default);
+ }
+
+ if (NoiseRemovalIterations > 0)
+ {
+ CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1),
+ (int)NoiseRemovalIterations, BorderType.Default, default);
+ }
+ }
+
+ if (!ReferenceEquals(image, null))
+ {
+ layer.LayerMat = image;
+ image.Dispose();
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ }
+
+ if (RemoveEmptyLayers)
+ {
+ List<uint> removeLayers = new List<uint>();
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
+ {
+ if (slicerFile[layerIndex].NonZeroPixelCount == 0)
+ {
+ removeLayers.Add(layerIndex);
+ }
+ }
+
+ if (removeLayers.Count > 0)
+ {
+ OperationLayerRemove.RemoveLayers(slicerFile, removeLayers, progress);
+ }
+ }
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs
index fa20389..0f909e5 100644
--- a/UVtools.Core/Operations/OperationResize.cs
+++ b/UVtools.Core/Operations/OperationResize.cs
@@ -8,6 +8,10 @@
using System;
using System.Text;
+using System.Threading.Tasks;
+using Emgu.CV;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
namespace UVtools.Core.Operations
@@ -15,10 +19,14 @@ namespace UVtools.Core.Operations
[Serializable]
public class OperationResize : Operation
{
+ #region Members
private decimal _x = 100;
private decimal _y = 100;
private bool _constrainXy;
private bool _isFade;
+ #endregion
+
+ #region Overrides
public override string Title => "Resize";
public override string Description =>
"Resize the model by a percentage in the X/Y plane.\n\n" +
@@ -47,8 +55,9 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString());
}
+ #endregion
-
+ #region Properties
public decimal X
{
get => _x;
@@ -57,13 +66,21 @@ namespace UVtools.Core.Operations
RaiseAndSetIfChanged(ref _x, value);
if (_constrainXy)
Y = value;
+ RaisePropertyChanged(nameof(XScale));
}
}
+ public decimal XScale => _x / 100m;
+ public decimal YScale => _y / 100m;
+
public decimal Y
{
get => _y;
- set => RaiseAndSetIfChanged(ref _y, value);
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _y, value)) return;
+ RaisePropertyChanged(nameof(YScale));
+ }
}
public bool ConstrainXY
@@ -84,7 +101,7 @@ namespace UVtools.Core.Operations
get => _isFade;
set => RaiseAndSetIfChanged(ref _isFade, value);
}
-
+ #endregion
public OperationResize()
{
@@ -125,5 +142,82 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ if (X == 1m && Y == 1m) return false;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+
+ decimal xSteps = Math.Abs(X - 1) / (LayerIndexEnd - LayerIndexStart);
+ decimal ySteps = Math.Abs(Y - 1) / (LayerIndexEnd - LayerIndexStart);
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var newX = X;
+ var newY = Y;
+ if (IsFade)
+ {
+ if (newX != 1m)
+ {
+
+ //maxIteration = Math.Max(iterationsStart, iterationsEnd);
+
+ newX = newX < 1m
+ ? newX + (layerIndex - LayerIndexStart) * xSteps
+ : newX - (layerIndex - LayerIndexStart) * xSteps;
+
+ // constrain
+ //iterations = Math.Min(Math.Max(1, iterations), maxIteration);
+ }
+
+ if (newY != 1m)
+ {
+
+ //maxIteration = Math.Max(iterationsStart, iterationsEnd);
+
+ newY = (newY < 1m
+ ? newY + (layerIndex - LayerIndexStart) * ySteps
+ : newY - (layerIndex - LayerIndexStart) * ySteps);
+
+ // constrain
+ //iterations = Math.Min(Math.Max(1, iterations), maxIteration);
+ }
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+
+ if (newX == 1.0m && newY == 1.0m) return;
+
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat, newX / 100m, newY / 100m);
+ slicerFile[layerIndex].LayerMat = mat;
+ });
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ var xScale = XScale;
+ var yScale = YScale;
+ if (arguments is not null && arguments.Length >= 2)
+ {
+ xScale = (decimal) arguments[0];
+ yScale = (decimal) arguments[1];
+ }
+
+ var target = GetRoiOrDefault(mat);
+ target.TransformFromCenter((double) xScale, (double) yScale);
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs
index 4d32762..f9a864e 100644
--- a/UVtools.Core/Operations/OperationRotate.cs
+++ b/UVtools.Core/Operations/OperationRotate.cs
@@ -7,13 +7,21 @@
*/
using System;
+using System.Threading.Tasks;
+using Emgu.CV;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationRotate : Operation
{
+ #region Members
private decimal _angleDegrees = 90;
+ #endregion
+
+ #region Overrides
public override string Title => "Rotate";
public override string Description =>
"Rotate the layers of the model.\n";
@@ -25,19 +33,22 @@ namespace UVtools.Core.Operations
$"Rotating layers {LayerIndexStart} through {LayerIndexEnd} {(AngleDegrees < 0 ? "counter-clockwise" : "clockwise")} by {Math.Abs(AngleDegrees)}°";
public override string ProgressAction => "Rotated layers";
-
- public decimal AngleDegrees
- {
- get => _angleDegrees;
- set => RaiseAndSetIfChanged(ref _angleDegrees, value);
- }
-
+
public override string ToString()
{
var result = $"[Angle: {Math.Abs(_angleDegrees)}º {(AngleDegrees < 0 ? "CCW" : "CW")}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
+ #endregion
+
+ #region Properties
+ public decimal AngleDegrees
+ {
+ get => _angleDegrees;
+ set => RaiseAndSetIfChanged(ref _angleDegrees, value);
+ }
+ #endregion
#region Equality
@@ -60,5 +71,36 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ Mat target = GetRoiOrDefault(mat);
+ target.Rotate((double)AngleDegrees);
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs
index 4dc4088..ca6577f 100644
--- a/UVtools.Core/Operations/OperationSolidify.cs
+++ b/UVtools.Core/Operations/OperationSolidify.cs
@@ -7,6 +7,13 @@
*/
using System;
+using System.Threading.Tasks;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
@@ -62,5 +69,61 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = slicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+ return true;
+ }
+
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ using Mat filteredMat = new Mat();
+ using VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
+ using Mat hierarchy = new Mat();
+ Mat target = GetRoiOrDefault(mat);
+
+ CvInvoke.Threshold(target, filteredMat, 127, 255, ThresholdType.Binary); // Clean AA
+ CvInvoke.FindContours(filteredMat, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple);
+ var arr = hierarchy.GetData();
+ for (int i = 0; i < contours.Size; i++)
+ {
+ if ((int)arr.GetValue(0, i, 2) != -1 || (int)arr.GetValue(0, i, 3) == -1) continue;
+ if (MinimumArea >= 1)
+ {
+ var rectangle = CvInvoke.BoundingRectangle(contours[i]);
+ if (AreaCheckType == AreaCheckTypes.More)
+ {
+ if (rectangle.GetArea() < MinimumArea) continue;
+ }
+ else
+ {
+ if (rectangle.GetArea() > MinimumArea) continue;
+ }
+
+ }
+
+ CvInvoke.DrawContours(target, contours, i, new MCvScalar(255), -1);
+ }
+
+ return true;
+ }
+
+ #endregion
}
}
diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs
index 143f173..f3776a4 100644
--- a/UVtools.Core/Operations/OperationThreshold.cs
+++ b/UVtools.Core/Operations/OperationThreshold.cs
@@ -7,17 +7,23 @@
*/
using System;
+using System.Threading.Tasks;
+using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
{
[Serializable]
public class OperationThreshold : Operation
{
+ #region Members
private byte _threshold = 127;
private byte _maximum = 255;
private ThresholdType _type = ThresholdType.Binary;
+ #endregion
+ #region Overrides
public override string Title => "Threshold pixels";
public override string Description =>
"Manipulate pixel values based on a threshold.\n\n" +
@@ -33,6 +39,15 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Thresholded layers";
+ public override string ToString()
+ {
+ var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Properties
public byte Threshold
{
get => _threshold;
@@ -52,14 +67,42 @@ namespace UVtools.Core.Operations
}
public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType));
+ #endregion
- public override string ToString()
+ #region Methods
+
+ public override bool Execute(FileFormat slicerFile, OperationProgress progress = null)
{
- var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = slicerFile[layerIndex].LayerMat)
+ {
+ Execute(mat);
+ slicerFile[layerIndex].LayerMat = mat;
+ }
+
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ });
+ progress.Token.ThrowIfCancellationRequested();
+
+ return true;
}
+ public override bool Execute(Mat mat, params object[] arguments)
+ {
+ Mat target = GetRoiOrDefault(mat);
+ CvInvoke.Threshold(target, target, Threshold, Maximum, Type);
+ return true;
+ }
+
+ #endregion
+
#region Equality
protected bool Equals(OperationThreshold other)
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 72c2fb8..73ec8e0 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
- <Version>2.0.0</Version>
+ <Version>2.1.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.Platforms/arch-x64/libcvextern.so b/UVtools.Platforms/arch-x64/libcvextern.so
new file mode 100644
index 0000000..b8b0375
--- /dev/null
+++ b/UVtools.Platforms/arch-x64/libcvextern.so
Binary files differ
diff --git a/UVtools.WPF/libcvextern.so b/UVtools.Platforms/linux-x64/libcvextern.so
index a75ef49..a75ef49 100644
--- a/UVtools.WPF/libcvextern.so
+++ b/UVtools.Platforms/linux-x64/libcvextern.so
Binary files differ
diff --git a/UVtools.Platforms/osx-x64/Info.plist b/UVtools.Platforms/osx-x64/Info.plist
new file mode 100644
index 0000000..bd45bf0
--- /dev/null
+++ b/UVtools.Platforms/osx-x64/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>CFBundleIconFile</key>
+ <string>UVtools.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.UVtools</string>
+ <key>CFBundleName</key>
+ <string>UVtools</string>
+ <key>CFBundleVersion</key>
+ <string>#VERSION</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.12</string>
+ <key>CFBundleExecutable</key>
+ <string>UVtools</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>#VERSION</string>
+ <key>NSHighResolutionCapable</key>
+ <true />
+ </dict>
+</plist>
diff --git a/UVtools.WPF/libcvextern.dylib b/UVtools.Platforms/osx-x64/libcvextern.dylib
index 1af1f8c..1af1f8c 100644
--- a/UVtools.WPF/libcvextern.dylib
+++ b/UVtools.Platforms/osx-x64/libcvextern.dylib
Binary files differ
diff --git a/UVtools.Platforms/rhel-x64/libcvextern.so b/UVtools.Platforms/rhel-x64/libcvextern.so
new file mode 100644
index 0000000..a75ef49
--- /dev/null
+++ b/UVtools.Platforms/rhel-x64/libcvextern.so
Binary files differ
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index fe21cb8..4d14ccc 100644
--- a/UVtools.WPF/App.axaml.cs
+++ b/UVtools.WPF/App.axaml.cs
@@ -38,7 +38,7 @@ namespace UVtools.WPF
public static AppVersionChecker VersionChecker { get; } = new AppVersionChecker();
- public static Size MaxWindowSize;
+ public static Size MaxWindowSize = Size.Empty;
public override void Initialize()
{
@@ -84,7 +84,6 @@ namespace UVtools.WPF
$"{e}", "UVtools can not run");
return;
}
-
desktop.MainWindow = MainWindow;
desktop.Exit += (sender, e)
@@ -189,17 +188,17 @@ namespace UVtools.WPF
public static string GetPrusaSlicerDirectory()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (OperatingSystem.IsWindows())
{
return $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}{Path.DirectorySeparatorChar}PrusaSlicer";
}
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ if (OperatingSystem.IsLinux())
{
return $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}{Path.DirectorySeparatorChar}.PrusaSlicer";
}
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ if (OperatingSystem.IsMacOS())
{
return string.Format("{0}{1}Library{1}Application Support{1}PrusaSlicer",
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), Path.DirectorySeparatorChar);
diff --git a/UVtools.WPF/Assets/Icons/code-branch-16x16.png b/UVtools.WPF/Assets/Icons/code-branch-16x16.png
new file mode 100644
index 0000000..e718637
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/code-branch-16x16.png
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/sun-16x16.png b/UVtools.WPF/Assets/Icons/sun-16x16.png
new file mode 100644
index 0000000..997ab7e
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/sun-16x16.png
Binary files differ
diff --git a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml
index fb3a6f4..02b0ad7 100644
--- a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml
@@ -8,90 +8,123 @@
<Grid
ColumnDefinitions="Auto,10,Auto,5,Auto"
- RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto"
+ RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto"
>
<TextBlock Text="Relief type:" VerticalAlignment="Center"/>
- <ComboBox
- Name="ReliefType"
+ <ComboBox Grid.Row="0"
Grid.Column="2"
Width="100"
SelectedItem="{Binding Operation.ReliefType}"
Items="{Binding Operation.RaftReliefItems}"/>
+
<TextBlock
Grid.Row="2"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"
+ Grid.Column="0"
+ Text="Ignore first:" VerticalAlignment="Center"/>
+
+ <NumericUpDown Grid.Row="2" Grid.Column="2"
+ Width="100"
+ Minimum="0"
+ Maximum="255"
+ Value="{Binding Operation.IgnoreFirstLayers}"/>
+ <TextBlock
+ Grid.Row="2" Grid.Column="4"
+ Text="layer(s)" VerticalAlignment="Center"/>
+
+ <TextBlock
+ Grid.Row="4"
+ Grid.Column="0"
+ IsVisible="{Binding !Operation.IsDecimate}"
+ Text="Pixel brightness:" VerticalAlignment="Center"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ Width="100"
+ Minimum="0"
+ Maximum="255"
+ IsVisible="{Binding !Operation.IsDecimate}"
+ Value="{Binding Operation.Brightness}"/>
+ <TextBlock
+ Grid.Row="4" Grid.Column="4"
+ IsVisible="{Binding !Operation.IsDecimate}"
+ Text="{Binding Operation.BrightnessPercent, StringFormat=\{0:0.00\}%}" VerticalAlignment="Center"/>
+
+
+
+ <TextBlock
+ Grid.Row="6"
+ IsVisible="{Binding !Operation.IsDecimate}"
Text="Supports margin:" VerticalAlignment="Center"/>
<TextBlock
- Grid.Row="2"
- IsVisible="{Binding #ReliefType.SelectedIndex}"
+ Grid.Row="6"
+ IsVisible="{Binding Operation.IsDecimate}"
ToolTip.Tip="Raft will be replaced by the present supports and then dilated by this value to thicken the supports and increase the adhesion.
&#x0a;Use large numbers with tiny supports for best adhesion."
Text="Dilate supports by:" VerticalAlignment="Center"/>
- <NumericUpDown Grid.Row="2" Grid.Column="2"
+ <NumericUpDown Grid.Row="6" Grid.Column="2"
Width="100"
Minimum="0"
Maximum="255"
Value="{Binding Operation.DilateIterations}"/>
<TextBlock
- Grid.Row="2" Grid.Column="4"
+ Grid.Row="6" Grid.Column="4"
Text="px" VerticalAlignment="Center"/>
<TextBlock
- Grid.Row="4"
+ Grid.Row="8"
Text="Wall margin:" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"
+ IsVisible="{Binding !Operation.IsDecimate}"
/>
- <NumericUpDown Grid.Row="4" Grid.Column="2"
+ <NumericUpDown Grid.Row="8" Grid.Column="2"
Width="100"
Minimum="1"
Maximum="255"
Value="{Binding Operation.WallMargin}"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
+ IsVisible="{Binding !Operation.IsDecimate}"/>
<TextBlock
- Grid.Row="4" Grid.Column="4"
+ Grid.Row="8" Grid.Column="4"
Text="px" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
+ IsVisible="{Binding !Operation.IsDecimate}"/>
<TextBlock
- Grid.Row="6"
+ Grid.Row="10"
Text="Hole diameter:" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"
+ IsVisible="{Binding Operation.IsRelief}"
/>
- <NumericUpDown Grid.Row="6" Grid.Column="2"
+ <NumericUpDown Grid.Row="10" Grid.Column="2"
Width="100"
Minimum="10"
Maximum="255"
Value="{Binding Operation.HoleDiameter}"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
+ IsVisible="{Binding Operation.IsRelief}"/>
<TextBlock
- Grid.Row="6" Grid.Column="4"
+ Grid.Row="10" Grid.Column="4"
Text="px" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
+ IsVisible="{Binding Operation.IsRelief}"/>
<TextBlock
- Grid.Row="8"
+ Grid.Row="12"
Text="Hole spacing:" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"
+ IsVisible="{Binding Operation.IsRelief}"
/>
- <NumericUpDown Grid.Row="8" Grid.Column="2"
+ <NumericUpDown Grid.Row="12" Grid.Column="2"
Width="100"
Minimum="10"
Maximum="255"
Value="{Binding Operation.HoleSpacing}"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
+ IsVisible="{Binding Operation.IsRelief}"/>
<TextBlock
- Grid.Row="8" Grid.Column="4"
+ Grid.Row="12" Grid.Column="4"
Text="px" VerticalAlignment="Center"
- IsVisible="{Binding !#ReliefType.SelectedIndex}"/>
-
+ IsVisible="{Binding Operation.IsRelief}"/>
+
</Grid>
</UserControl>
diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml
new file mode 100644
index 0000000..cce0f0e
--- /dev/null
+++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml
@@ -0,0 +1,66 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Tools.ToolRedrawModelControl">
+ <StackPanel Spacing="10">
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,100,5,300,5,Auto">
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ ToolTip.Tip="Select the sliced file without supports and raft. (Model B)"
+ VerticalAlignment="Center"
+ Text="Body file:"/>
+ <TextBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3"
+ ToolTip.Tip="Select the sliced file without supports and raft. (Model B)"
+ IsReadOnly="True"
+ Watermark="Select the sliced file without supports and raft. (Model B)"
+ Text="{Binding Operation.FilePath}"/>
+ <Button Grid.Row="0" Grid.Column="6"
+ ToolTip.Tip="Select the sliced file without supports and raft. (Model B)"
+ Command="{Binding ImportFile}">
+ <Image Source="/Assets/Icons/file-import-16x16.png"/>
+ </Button>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Redraw:"/>
+ <ComboBox Grid.Row="2" Grid.Column="2"
+ SelectedItem="{Binding Operation.RedrawType}"
+ Items="{Binding Operation.RedrawTypesItems}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Brightness:"/>
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ ClipValueToMinMax="True"
+ Minimum="0"
+ Maximum="255"
+ Increment="1"
+ Value="{Binding Operation.Brightness}"/>
+ <TextBlock Grid.Row="4" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="{Binding Operation.BrightnessPercent, StringFormat=\{0:0.00\}%}"/>
+
+ <StackPanel Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="3"
+ Orientation="Horizontal" Spacing="10">
+ <CheckBox
+ VerticalAlignment="Center"
+ IsVisible="{Binding !Operation.RedrawType}"
+ IsChecked="{Binding Operation.ContactPointsOnly}"
+ ToolTip.Tip="If enabled, it will only redraw the supports with physical contact points within the model. (Slower)"
+ Content="Only the contact supports"/>
+
+ <CheckBox
+ VerticalAlignment="Center"
+ IsVisible="{Binding Operation.ContactPointsOnly}"
+ IsChecked="{Binding Operation.IgnoreContactLessPixels}"
+ ToolTip.Tip="If enabled, all supports pixels with no physical contact with the model will be ignored and maintain the same original brightness."
+ Content="Ignore contact-less pixels"/>
+ </StackPanel>
+
+ </Grid>
+
+
+ </StackPanel>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs
new file mode 100644
index 0000000..83934ff
--- /dev/null
+++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using UVtools.Core.FileFormats;
+using UVtools.Core.Operations;
+
+namespace UVtools.WPF.Controls.Tools
+{
+ public class ToolRedrawModelControl : ToolControl
+ {
+ public OperationRedrawModel Operation => BaseOperation as OperationRedrawModel;
+ public ToolRedrawModelControl()
+ {
+ InitializeComponent();
+ BaseOperation = new OperationRedrawModel();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public async void ImportFile()
+ {
+ var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
+ var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] };
+ for (int i = 0; i < filters.Count; i++)
+ {
+ if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue;
+ orderedFilters.Add(filters[i]);
+ }
+
+ var dialog = new OpenFileDialog
+ {
+ AllowMultiple = false,
+ Filters = orderedFilters,
+ Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile
+ };
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length <= 0) return;
+ if (FileFormat.FindByExtension(files[0], true) is null) return;
+ Operation.FilePath = files[0];
+ }
+ }
+}
diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs
index 53cef90..e1b2f76 100644
--- a/UVtools.WPF/MainWindow.Issues.cs
+++ b/UVtools.WPF/MainWindow.Issues.cs
@@ -166,7 +166,7 @@ namespace UVtools.WPF
if (layersRemove.Count > 0)
{
- SlicerFile.LayerManager.RemoveLayers(layersRemove);
+ OperationLayerRemove.RemoveLayers(SlicerFile, layersRemove);
result = true;
}
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index 51c2286..78de0d8 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -22,6 +22,7 @@ using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using UVtools.Core;
using UVtools.Core.Extensions;
@@ -131,6 +132,14 @@ namespace UVtools.WPF
},
new MenuItem
{
+ Tag = new OperationRedrawModel(),
+ Icon = new Avalonia.Controls.Image
+ {
+ Source = new Bitmap(App.GetAsset("/Assets/Icons/sun-16x16.png"))
+ }
+ },
+ new MenuItem
+ {
Tag = new OperationThreshold(),
Icon = new Avalonia.Controls.Image
{
@@ -401,8 +410,6 @@ namespace UVtools.WPF
//this.AttachDevTools();
#endif
- UpdateMaxWindowsSize();
-
App.ThemeSelector?.EnableThemes(this);
InitInformation();
InitIssues();
@@ -422,7 +429,7 @@ namespace UVtools.WPF
{
foreach (var menuTool in menuItem)
{
- if (!(menuTool.Tag is Operation operation)) continue;
+ if (menuTool.Tag is not Operation operation) continue;
menuTool.Header = operation.Title;
menuTool.Click += async (sender, args) => await ShowRunOperation(operation.GetType());
}
@@ -439,20 +446,23 @@ namespace UVtools.WPF
}
};*/
//PropertyChanged += OnPropertyChanged;
- var clientSizeObs = this.GetObservable(ClientSizeProperty);
- clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
- var windowStateObs = this.GetObservable(WindowStateProperty);
- windowStateObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
-
+
UpdateTitle();
+ UpdateMaxWindowsSize();
- if (Settings.General.StartMaximized
+ if (Settings.General.StartMaximized
|| ClientSize.Width > App.MaxWindowSize.Width
|| ClientSize.Height > App.MaxWindowSize.Height)
{
WindowState = WindowState.Maximized;
}
+ var clientSizeObs = this.GetObservable(ClientSizeProperty);
+ clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
+ var windowStateObs = this.GetObservable(WindowStateProperty);
+ windowStateObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
+
+
DataContext = this;
AddHandler(DragDrop.DropEvent, (sender, e) =>
@@ -463,14 +473,22 @@ namespace UVtools.WPF
public void UpdateMaxWindowsSize()
{
- App.MaxWindowSize = new System.Drawing.Size(Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(Screens.Primary.WorkingArea.Width / Screens.Primary.PixelDensity) : Screens.Primary.WorkingArea.Width,
- Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(Screens.Primary.WorkingArea.Height / Screens.Primary.PixelDensity) : Screens.Primary.WorkingArea.Height);
+ /*Console.WriteLine($"Settings is null?: {Settings is null}");
+ Console.WriteLine($"Settings.General is null?: {Settings.General is null}");
+ Console.WriteLine($"Screens is null?: {Screens is null}");
+ Console.WriteLine($"ScreenCount: {Screens.ScreenCount}");
+ Console.WriteLine($"Screens.Primary is null?: {Screens.Primary is null}");*/
+ var screen = Screens.Primary ?? Screens.All[0];
+ App.MaxWindowSize = new System.Drawing.Size(
+ Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Width / screen.PixelDensity) : screen.WorkingArea.Width,
+ Settings.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Height / screen.PixelDensity) : screen.WorkingArea.Height);
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
- Program.ProgramStartupTime.Stop();
+
+
AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds);
if (Settings.General.CheckForUpdatesOnStartup)
@@ -489,6 +507,7 @@ namespace UVtools.WPF
UpdateTitle();
return true;
}, TimeSpan.FromSeconds(1));
+ Program.ProgramStartupTime.Stop();
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -804,6 +823,12 @@ namespace UVtools.WPF
"Changelog:\n" +
$"{VersionChecker.Changelog}", $"Update UVtools to v{VersionChecker.Version}?", ButtonEnum.YesNoCancel);
+
+ if (result == ButtonResult.No || OperatingSystem.IsMacOS())
+ {
+ App.OpenBrowser(VersionChecker.UrlLatestRelease);
+ return;
+ }
if (result == ButtonResult.Yes)
{
IsGUIEnabled = false;
@@ -832,11 +857,7 @@ namespace UVtools.WPF
return;
}
- if (result == ButtonResult.No)
- {
- App.OpenBrowser(VersionChecker.UrlLatestRelease);
- return;
- }
+
}
#endregion
@@ -936,7 +957,7 @@ namespace UVtools.WPF
ClipboardManager.Instance.Init(SlicerFile);
- if (!(SlicerFile.ConvertToFormats is null))
+ if (SlicerFile.ConvertToFormats is not null)
{
List<MenuItem> menuItems = new List<MenuItem>();
foreach (var fileFormatType in SlicerFile.ConvertToFormats)
@@ -1290,18 +1311,13 @@ namespace UVtools.WPF
switch (baseOperation)
{
case OperationEditParameters operation:
- /*foreach (var modifier in operation.Modifiers.Where(modifier => modifier.HasChanged))
- {
- SlicerFile.SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
- }*/
- SlicerFile.EditPrintParameters(operation);
+ operation.Execute(SlicerFile);
RefreshProperties();
RefreshCurrentLayerData();
ResetDataContext();
CanSave = true;
-
- return false;
+ return true;
case OperationRepairLayers operation:
if (Issues is null)
{
@@ -1319,7 +1335,7 @@ namespace UVtools.WPF
}
operation.Issues = Issues.ToList();
-
+ operation.IslandDetectionConfig = GetIslandDetectionConfiguration();
break;
}
@@ -1331,110 +1347,13 @@ namespace UVtools.WPF
ShowProgressWindow(baseOperation.ProgressTitle);
backup = SlicerFile.LayerManager.Clone();
- /*var backup = new Layer[baseOperation.LayerRangeCount];
- uint i = 0;
- for (uint layerIndex = baseOperation.LayerIndexStart; layerIndex <= baseOperation.LayerIndexEnd; layerIndex++)
- {
- backup[i++] = SlicerFile[layerIndex].Clone();
- }*/
-
try
{
- switch (baseOperation)
- {
- // Tools
- case OperationRepairLayers operation:
- operation.IslandDetectionConfig = GetIslandDetectionConfiguration();
- SlicerFile.LayerManager.RepairLayers(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationMove operation:
- SlicerFile.LayerManager.Move(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationResize operation:
- SlicerFile.LayerManager.Resize(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationFlip operation:
- SlicerFile.LayerManager.Flip(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationRotate operation:
- SlicerFile.LayerManager.Rotate(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationSolidify operation:
- SlicerFile.LayerManager.Solidify(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationMorph operation:
- SlicerFile.LayerManager.Morph(operation, BorderType.Default, new MCvScalar(), ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationRaftRelief operation:
- SlicerFile.LayerManager.RaftRelief(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationThreshold operation:
- SlicerFile.LayerManager.ThresholdPixels(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationArithmetic operation:
- SlicerFile.LayerManager.Arithmetic(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationMask operation:
- SlicerFile.LayerManager.Mask(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationPixelDimming operation:
- SlicerFile.LayerManager.PixelDimming(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationInfill operation:
- SlicerFile.LayerManager.Infill(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationBlur operation:
- SlicerFile.LayerManager.Blur(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
-
- case OperationChangeResolution operation:
- SlicerFile.LayerManager.ChangeResolution(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationLayerReHeight operation:
- SlicerFile.LayerManager.ReHeight(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationPattern operation:
- SlicerFile.LayerManager.Pattern(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- // Actions
- case OperationLayerImport operation:
- SlicerFile.LayerManager.Import(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationLayerClone operation:
- SlicerFile.LayerManager.CloneLayer(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationLayerRemove operation:
- SlicerFile.LayerManager.RemoveLayers(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
-
- // Calibrators
- case OperationCalibrateElephantFoot operation:
- SlicerFile.LayerManager.CalibrateElephantFoot(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationCalibrateXYZAccuracy operation:
- SlicerFile.LayerManager.CalibrateXYZAccuracy(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationCalibrateTolerance operation:
- SlicerFile.LayerManager.CalibrateTolerance(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
- case OperationCalibrateGrayscale operation:
- SlicerFile.LayerManager.CalibrateGrayscale(operation, ProgressWindow.RestartProgress(operation.CanCancel));
- break;
-
- default:
- throw new NotImplementedException();
- }
-
- return true;
+ return baseOperation.Execute(SlicerFile, ProgressWindow.RestartProgress(baseOperation.CanCancel));
}
catch (OperationCanceledException)
{
SlicerFile.LayerManager = backup;
- /*i = 0;
- for (uint layerIndex = baseOperation.LayerIndexStart; layerIndex <= baseOperation.LayerIndexEnd; layerIndex++)
- {
- SlicerFile[layerIndex] = backup[i++];
- }*/
}
catch (Exception ex)
{
@@ -1467,7 +1386,7 @@ namespace UVtools.WPF
}
}
- return true;
+ return result;
}
#endregion
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 79e6903..eaa6128 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -159,85 +159,82 @@ namespace UVtools.WPF.Structures
progress.ItemName = "Megabytes";
try
{
- using (var client = new WebClient())
- {
- var path = Path.GetTempPath();
- DownloadedFile = Path.Combine(path, Filename);
- Debug.WriteLine($"Downloading to: {DownloadedFile}");
+ using var client = new WebClient();
+ var path = Path.GetTempPath();
+ DownloadedFile = Path.Combine(path, Filename);
+ Debug.WriteLine($"Downloading to: {DownloadedFile}");
- client.DownloadProgressChanged += (sender, e) =>
+ client.DownloadProgressChanged += (sender, e) =>
+ {
+ progress.Reset("Megabytes", (uint)e.TotalBytesToReceive / 1048576, (uint)e.BytesReceived / 1048576);
+ };
+ client.DownloadFileCompleted += (sender, e) =>
+ {
+ progress.Reset("Extracting");
+ if (OperatingSystem.IsWindows())
{
- progress.Reset("Megabytes", (uint)e.TotalBytesToReceive / 1048576, (uint)e.BytesReceived / 1048576);
- };
- client.DownloadFileCompleted += (sender, e) =>
+ App.StartProcess(DownloadedFile);
+ Environment.Exit(0);
+ }
+ else
{
- progress.Reset("Extracting");
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- App.StartProcess(DownloadedFile);
- Environment.Exit(0);
- }
- else
+ string upgradeFolder = "UPDATED_VERSION";
+ var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
+ using (var stream = File.Open(DownloadedFile, FileMode.Open))
{
- string upgradeFolder = "UPDATED_VERSION";
- var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
- using (var stream = File.Open(DownloadedFile, FileMode.Open))
- {
- using (ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Read))
- {
- zip.ExtractToDirectory(targetDir, true);
- }
- }
-
- File.Delete(DownloadedFile);
-
- string upgradeFileName = $"{About.Software}_upgrade.sh";
- var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
- using (var stream = File.CreateText(upgradeFile))
+ using (ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Read))
{
- stream.WriteLine("#!/bin/bash");
- stream.WriteLine($"echo {About.Software} v{App.Version} updater script");
- stream.WriteLine($"cd '{App.ApplicationPath}'");
- stream.WriteLine($"killall {About.Software}");
- stream.WriteLine("sleep 0.5");
- stream.WriteLine($"cp -fR {upgradeFolder}/* .");
- stream.WriteLine($"rm -fr {upgradeFolder}");
- stream.WriteLine("sleep 0.5");
- //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
- stream.WriteLine($"if [ -f '{About.Software}' ]; then");
- stream.WriteLine($" ./{About.Software} &");
- stream.WriteLine("else");
- stream.WriteLine($" dotnet {About.Software}.dll &");
- stream.WriteLine("fi");
- stream.WriteLine($"rm -f {upgradeFileName}");
- //stream.WriteLine("exit");
- stream.Close();
+ zip.ExtractToDirectory(targetDir, true);
}
-
- App.StartProcess("bash", $"\"{upgradeFile}\"");
-
- //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
- Environment.Exit(0);
}
- lock (e.UserState)
+ File.Delete(DownloadedFile);
+
+ string upgradeFileName = $"{About.Software}_upgrade.sh";
+ var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
+ using (var stream = File.CreateText(upgradeFile))
{
- //releases blocked thread
- Monitor.Pulse(e.UserState);
+ stream.WriteLine("#!/bin/bash");
+ stream.WriteLine($"echo {About.Software} v{App.Version} updater script");
+ stream.WriteLine($"cd '{App.ApplicationPath}'");
+ stream.WriteLine($"killall {About.Software}");
+ stream.WriteLine("sleep 0.5");
+ stream.WriteLine($"cp -fR {upgradeFolder}/* .");
+ stream.WriteLine($"rm -fr {upgradeFolder}");
+ stream.WriteLine("sleep 0.5");
+ //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
+ stream.WriteLine($"if [ -f '{About.Software}' ]; then");
+ stream.WriteLine($" ./{About.Software} &");
+ stream.WriteLine("else");
+ stream.WriteLine($" dotnet {About.Software}.dll &");
+ stream.WriteLine("fi");
+ stream.WriteLine($"rm -f {upgradeFileName}");
+ //stream.WriteLine("exit");
+ stream.Close();
}
- };
+ App.StartProcess("bash", $"\"{upgradeFile}\"");
+
+ //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
+ Environment.Exit(0);
+ }
- var syncObject = new object();
-
- lock (syncObject)
+ lock (e.UserState)
{
- client.DownloadFileAsync(new Uri(DownloadLink), DownloadedFile, syncObject);
- //This would block the thread until download completes
- Monitor.Wait(syncObject);
+ //releases blocked thread
+ Monitor.Pulse(e.UserState);
}
+ };
+
+ var syncObject = new object();
+
+ lock (syncObject)
+ {
+ client.DownloadFileAsync(new Uri(DownloadLink), DownloadedFile, syncObject);
+ //This would block the thread until download completes
+ Monitor.Wait(syncObject);
}
}
catch (Exception e)
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
index 57e5c72..257b213 100644
--- a/UVtools.WPF/Structures/OperationProfiles.cs
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -32,6 +32,7 @@ namespace UVtools.WPF.Structures
//[XmlElement(typeof(OperationMask))]
[XmlElement(typeof(OperationMorph))]
[XmlElement(typeof(OperationRaftRelief))]
+ [XmlElement(typeof(OperationRedrawModel))]
//[XmlElement(typeof(OperationMove))]
//[XmlElement(typeof(OperationPattern))]
[XmlElement(typeof(OperationPixelDimming))]
@@ -42,6 +43,7 @@ namespace UVtools.WPF.Structures
[XmlElement(typeof(OperationThreshold))]
[XmlElement(typeof(OperationCalibrateElephantFoot))]
[XmlElement(typeof(OperationCalibrateXYZAccuracy))]
+ [XmlElement(typeof(OperationCalibrateTolerance))]
[XmlElement(typeof(OperationCalibrateGrayscale))]
public List<Operation> Operations { get; internal set; } = new List<Operation>();
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 15aaf26..3c153db 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>2.0.0</Version>
+ <Version>2.1.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -48,9 +48,6 @@
<None Remove="Assets\Icons\UVtools.ico" />
</ItemGroup>
<ItemGroup>
- <None Update="libcvextern.dylib">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
<None Update="libcvextern.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -88,6 +85,9 @@
<Compile Update="Controls\Tools\ToolLayerRemoveControl.axaml.cs">
<DependentUpon>ToolLayerRemoveControl.axaml</DependentUpon>
</Compile>
+ <Compile Update="Controls\Tools\ToolRedrawModelControl.axaml.cs">
+ <DependentUpon>ToolRedrawModelControl.axaml</DependentUpon>
+ </Compile>
<Compile Update="Controls\Tools\ToolResizeControl.axaml.cs">
<DependentUpon>ToolResizeControl.axaml</DependentUpon>
</Compile>
diff --git a/UVtools.WPF/UVtools.sh b/UVtools.WPF/UVtools.sh
index 4bac434..e4516b4 100644
--- a/UVtools.WPF/UVtools.sh
+++ b/UVtools.WPF/UVtools.sh
@@ -1,3 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")"
-dotnet UVtools.dll \ No newline at end of file
+[ -f UVtools ] && ./UVtools || dotnet UVtools.dll