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>2022-11-10 07:09:00 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-11-10 07:09:00 +0300
commit8e4995a6a5f35bbee9fa54108b2743fd7fe59f18 (patch)
tree9f68381c2a9b66d2512aef8b658bca3f336d7252
parent051f11bb6e723e9ccc624abdaa69e5d8750ffa01 (diff)
v3.8.3
- **UVtoolsCmd:** - **print-properties:** - (Change) `-b`, `--base` option to `-a`, `-all` to indicate all properties and sub-properties, now defaults to only show base properties - (Add) `-r`, `--range` option to prints only the matching layer(s) index(es) in a range - (Add) `-i`, `--indexes` option to prints only the matching layer(s) index(es) - (Add) Command: `set-properties <input-file> <property=value> Set properties in a file or to it layers with new values` - (Add) Command: `print-issues <input-file> Detect and print issues in a file` - (Add) New option to the `run` command: `-p, --property <property=value> Set a property with a new value (Compatible with operations only)` - (Remove) Command: `print-layers` as it has been moved to `print-properties`, use `-r :` to obtain same result as default on `print-layers` - **Issues:** - (Fix) Issues groups with only one issue was displaying the wrong area value - (Fix) Volume incorrectly calculated, resulting in a high value for group of issues - (Fix) Incorrect calculation of the bounding rectangle for a group of issues - **Repair layers:** - (Add) Switch to opt between "Re-detect the selected issues before repair" and "Use and repair the previous detected issues" (Default) - (Improvement) Do not allow to run the tool if there are no detected issues when the option "Use and repair the previous detected issues" is selected - (Improvement) Linux: Recompile libcvextern.so on a older system to be able to run on both older and newest system (#603) - (Upgrade) .NET from 6.0.10 to 6.0.11
-rw-r--r--CHANGELOG.md21
-rw-r--r--README.md20
-rw-r--r--RELEASE_NOTES.md39
-rw-r--r--Scripts/install-dependencies.sh19
-rw-r--r--Scripts/install-uvtools.sh13
-rw-r--r--UVtools.Cmd/Program.cs6
-rw-r--r--UVtools.Cmd/ReflectionPropertyValue.cs53
-rw-r--r--UVtools.Cmd/Symbols/GlobalOptions.cs2
-rw-r--r--UVtools.Cmd/Symbols/PrintGCodeCommand.cs1
-rw-r--r--UVtools.Cmd/Symbols/PrintIssuesCommand.cs91
-rw-r--r--UVtools.Cmd/Symbols/PrintLayersCommand.cs47
-rw-r--r--UVtools.Cmd/Symbols/PrintMachines.cs2
-rw-r--r--UVtools.Cmd/Symbols/PrintPropertiesCommand.cs151
-rw-r--r--UVtools.Cmd/Symbols/RunCommand.cs52
-rw-r--r--UVtools.Cmd/Symbols/SetPropertiesCommand.cs134
-rw-r--r--UVtools.Cmd/Symbols/SetThumbnailCommand.cs15
-rw-r--r--UVtools.Cmd/UVtools.Cmd.csproj2
-rw-r--r--UVtools.Core/Extensions/ReflectionExtensions.cs125
-rw-r--r--UVtools.Core/FileFormats/CWSFile.cs8
-rw-r--r--UVtools.Core/FileFormats/ChituboxZipFile.cs2
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs68
-rw-r--r--UVtools.Core/FileFormats/GenericZIPFile.cs2
-rw-r--r--UVtools.Core/FileFormats/JXSFile.cs2
-rw-r--r--UVtools.Core/FileFormats/SL1File.cs2
-rw-r--r--UVtools.Core/FileFormats/VDAFile.cs14
-rw-r--r--UVtools.Core/FileFormats/ZCodeFile.cs10
-rw-r--r--UVtools.Core/Helpers.cs57
-rw-r--r--UVtools.Core/Layers/Issue.cs5
-rw-r--r--UVtools.Core/Layers/IssueOfContours.cs2
-rw-r--r--UVtools.Core/Layers/IssueOfPoints.cs2
-rw-r--r--UVtools.Core/Layers/LayerIssueConfiguration.cs197
-rw-r--r--UVtools.Core/Layers/MainIssue.cs39
-rw-r--r--UVtools.Core/Managers/IssueManager.cs71
-rw-r--r--UVtools.Core/Managers/SuggestionManager.cs2
-rw-r--r--UVtools.Core/Objects/ExposureItem.cs2
-rw-r--r--UVtools.Core/Objects/GenericFileRepresentation.cs2
-rw-r--r--UVtools.Core/Objects/KernelConfiguration.cs2
-rw-r--r--UVtools.Core/Operations/Operation.cs2
-rw-r--r--UVtools.Core/Operations/OperationBlur.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalculator.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateElephantFoot.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExposureFinder.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateGrayscale.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateLiftHeight.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateStressTower.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateTolerance.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs2
-rw-r--r--UVtools.Core/Operations/OperationChangeResolution.cs2
-rw-r--r--UVtools.Core/Operations/OperationDoubleExposure.cs2
-rw-r--r--UVtools.Core/Operations/OperationDynamicLayerHeight.cs2
-rw-r--r--UVtools.Core/Operations/OperationDynamicLifts.cs2
-rw-r--r--UVtools.Core/Operations/OperationEditParameters.cs2
-rw-r--r--UVtools.Core/Operations/OperationFadeExposureTime.cs2
-rw-r--r--UVtools.Core/Operations/OperationFlip.cs2
-rw-r--r--UVtools.Core/Operations/OperationIPrintedThisFile.cs2
-rw-r--r--UVtools.Core/Operations/OperationInfill.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerArithmetic.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerClone.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportGif.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportHeatMap.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportHtml.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportImage.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportMesh.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerExportSkeleton.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerReHeight.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerRemove.cs2
-rw-r--r--UVtools.Core/Operations/OperationLightBleedCompensation.cs2
-rw-r--r--UVtools.Core/Operations/OperationLithophane.cs2
-rw-r--r--UVtools.Core/Operations/OperationMask.cs2
-rw-r--r--UVtools.Core/Operations/OperationMorph.cs2
-rw-r--r--UVtools.Core/Operations/OperationMove.cs2
-rw-r--r--UVtools.Core/Operations/OperationPCBExposure.cs2
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs2
-rw-r--r--UVtools.Core/Operations/OperationPixelArithmetic.cs2
-rw-r--r--UVtools.Core/Operations/OperationPixelDimming.cs2
-rw-r--r--UVtools.Core/Operations/OperationRaftRelief.cs2
-rw-r--r--UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs2
-rw-r--r--UVtools.Core/Operations/OperationRedrawModel.cs2
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs97
-rw-r--r--UVtools.Core/Operations/OperationResize.cs2
-rw-r--r--UVtools.Core/Operations/OperationRotate.cs2
-rw-r--r--UVtools.Core/Operations/OperationScripting.cs2
-rw-r--r--UVtools.Core/Operations/OperationSolidify.cs2
-rw-r--r--UVtools.Core/Operations/OperationThreshold.cs2
-rw-r--r--UVtools.Core/Operations/OperationTimelapse.cs2
-rw-r--r--UVtools.Core/UVtools.Core.csproj4
-rw-r--r--UVtools.Installer/Code/HeatGeneratedFileList.wxs6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml8
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs6
-rw-r--r--UVtools.WPF/MainWindow.Issues.cs59
-rw-r--r--UVtools.WPF/MainWindow.axaml2
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs28
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs1
-rw-r--r--UVtools.WPF/Structures/Color.cs2
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs2
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--UVtools.WPF/UserSettings.cs18
-rw-r--r--build/libcvextern.sh14
-rw-r--r--build/platforms/linux-x64/libcvextern.zipbin23182686 -> 23418475 bytes
-rw-r--r--build/platforms/linux-x64/libcvextern22.04.zipbin0 -> 23182686 bytes
-rw-r--r--documentation/UVtools.Core.xml69
103 files changed, 1202 insertions, 496 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be4f925..5b5bf66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+## 10/11/2022 - v3.8.3
+
+- **UVtoolsCmd:**
+ - **print-properties:**
+ - (Change) `-b`, `--base` option to `-a`, `-all` to indicate all properties and sub-properties, now defaults to only show base properties
+ - (Add) `-r`, `--range` option to prints only the matching layer(s) index(es) in a range
+ - (Add) `-i`, `--indexes` option to prints only the matching layer(s) index(es)
+ - (Add) Command: `set-properties <input-file> <property=value> Set properties in a file or to it layers with new values`
+ - (Add) Command: `print-issues <input-file> Detect and print issues in a file`
+ - (Add) New option to the `run` command: `-p, --property <property=value> Set a property with a new value (Compatible with operations only)`
+ - (Remove) Command: `print-layers` as it has been moved to `print-properties`, use `-r :` to obtain same result as default on `print-layers`
+- **Issues:**
+ - (Fix) Issues groups with only one issue was displaying the wrong area value
+ - (Fix) Volume incorrectly calculated, resulting in a high value for group of issues
+ - (Fix) Incorrect calculation of the bounding rectangle for a group of issues
+- **Repair layers:**
+ - (Add) Switch to opt between "Re-detect the selected issues before repair" and "Use and repair the previous detected issues" (Default)
+ - (Improvement) Do not allow to run the tool if there are no detected issues when the option "Use and repair the previous detected issues" is selected
+- (Improvement) Linux: Recompile libcvextern.so on a older system to be able to run on both older and newest system (#603)
+- (Upgrade) .NET from 6.0.10 to 6.0.11
+
## 05/11/2022 - v3.8.2
- **Import thumbnails:**
diff --git a/README.md b/README.md
index 0f821dd..54f50ec 100644
--- a/README.md
+++ b/README.md
@@ -190,15 +190,17 @@ Options:
-?, -h, --help Show help and usage information
Commands:
- run <input-file> <files> Run operations and/or scripts
- convert <input-file> <target-type/ext> <output-file> Convert input file into a output file format by a known type or extension []
- extract <input-file> <output-folder> Extract file contents to a folder []
- copy-parameters <input-file> <target-files> Copy print parameters from one file to another
- set-preview, set-thumbnail <input-file> <file path|layer index|:random-layer|:heatmap> Sets and replace thumbnail(s) in the file [default: :heatmap]
- print-properties <input-file> Prints available properties
- print-layers <input-file> Prints layer(s) properties
- print-gcode <input-file> Prints the gcode of the file if available
- print-machines Prints machine settings
+ set-properties <input-file> <property=value> Set properties in a file or to it layers with new values
+ run <input-file> <files> Run operations and/or scripts
+ convert <input-file> <target-type/ext> <output-file> Convert input file into a output file format by a known type or extension []
+ extract <input-file> <output-folder> Extract file contents to a folder []
+ copy-parameters <input-file> <target-files> Copy print parameters from one file to another
+ set-preview, set-thumbnail <input-file> <file path|layer Sets and replace thumbnail(s) in the file [default: :heatmap]
+ index|:random-layer|:heatmap>
+ print-issues <input-file> Detect and print issues
+ print-properties <input-file> Prints available properties
+ print-gcode <input-file> Prints the gcode of the file if available
+ print-machines Prints machine settings
```
Note: On each command you can use -? to see specific command help and extra options
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 5099425..4e64784 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,22 +1,19 @@
-- **Import thumbnails:**
- - (Add) Import from file
- - (Add) Import from file (Replace all)
- - (Add) Import from current layer
- - (Add) Import from current layer (Replace all)
- - (Add) Import from random layer
- - (Add) Import from random layer (Replace all)
- - (Add) Import from heatmap
- - (Add) Import from heatmap (Replace all)
- - (Fix) Import from file could load in any image color type, resulting in wrong encoding on file save
-- **Auto-upgrade script:** (Will only take effect on the next release)
- - (Improvement) Add some marker/debug messages
- - (Improvement) On generic Linux and macOS try to rename the folder if contain the version on it name to the upgraded version name
- - (Improvement) Linux AppImage upgrades now renames to UVtools.AppImage
- - (Improvement) Re-open the program with the current loaded file
-- (Add) UVtoolsCmd: `set-preview, set-thumbnail <input-file> <file path|layer index|:random-layer|:heatmap> Sets and replace thumbnail(s) in the file [default: :heatmap]`. (#599)
- Use `UVtoolsCmd set-preview -?` to view the full documentation
-- (Improvement) Export layers to mesh: Write the file to a temporary location and move it to the target location when complete with success
-- (Fix) Error when opening a file with light calculated issues that cause a complete issue detection and when there are auto applied suggestions that modify the layer count (#598)
-- (Fix) Auto applying suggestions was not triggering a UI properties refresh, causing some values to show outdated
-- (Fix) PCB exposure: Bad parsing of macros when the ending `%` is alone in a new line (#600)
+- **UVtoolsCmd:**
+ - **print-properties:**
+ - (Change) `-b`, `--base` option to `-a`, `-all` to indicate all properties and sub-properties, now defaults to only show base properties
+ - (Add) `-r`, `--range` option to prints only the matching layer(s) index(es) in a range
+ - (Add) `-i`, `--indexes` option to prints only the matching layer(s) index(es)
+ - (Add) Command: `set-properties <input-file> <property=value> Set properties in a file or to it layers with new values`
+ - (Add) Command: `print-issues <input-file> Detect and print issues in a file`
+ - (Add) New option to the `run` command: `-p, --property <property=value> Set a property with a new value (Compatible with operations only)`
+ - (Remove) Command: `print-layers` as it has been moved to `print-properties`, use `-r :` to obtain same result as default on `print-layers`
+- **Issues:**
+ - (Fix) Issues groups with only one issue was displaying the wrong area value
+ - (Fix) Volume incorrectly calculated, resulting in a high value for group of issues
+ - (Fix) Incorrect calculation of the bounding rectangle for a group of issues
+- **Repair layers:**
+ - (Add) Switch to opt between "Re-detect the selected issues before repair" and "Use and repair the previous detected issues" (Default)
+ - (Improvement) Do not allow to run the tool if there are no detected issues when the option "Use and repair the previous detected issues" is selected
+- (Improvement) Linux: Recompile libcvextern.so on a older system to be able to run on both older and newest system (#603)
+- (Upgrade) .NET from 6.0.10 to 6.0.11
diff --git a/Scripts/install-dependencies.sh b/Scripts/install-dependencies.sh
index cd0f5ed..0b1dd21 100644
--- a/Scripts/install-dependencies.sh
+++ b/Scripts/install-dependencies.sh
@@ -30,23 +30,26 @@ if [ "${OSTYPE:0:6}" == "darwin" ]; then
#brew analytics off
#brew install git cmake mono-libgdiplus
#brew install --cask dotnet
-elif testcmd apt-get; then
+elif testcmd apt; then
osVariant="debian"
apt update
- apt install -y libdc1394-22 libopenexr24
- apt install -y libdc1394-25 libopenexr25
- apt install -y libjpeg-dev libpng-dev libgeotiff-dev libgeotiff5 libavcodec-dev libavformat-dev libswscale-dev libtbb-dev libgl1-mesa-dev libgdiplus
+ #apt install -y libdc1394-22 libopenexr24
+ #apt install -y libdc1394-25 libopenexr25
+ #apt install -y libjpeg-dev libpng-dev libgeotiff-dev libgeotiff5 libavcodec-dev libavformat-dev libswscale-dev libtbb-dev libgl1-mesa-dev libgdiplus
+ apt install -y libjpeg-dev libpng-dev libgeotiff-dev libgeotiff5 libtbb-dev libgdiplus
# mini only requires: libgdiplus libgeotiff-dev libgeotiff5
elif testcmd pacman; then
osVariant="arch"
pacman -Syu
- pacman -S openjpeg2 libjpeg-turbo libpng libgeotiff libdc1394 ffmpeg openexr tbb libgdiplus
+ #pacman -S openjpeg2 libjpeg-turbo libpng libgeotiff libdc1394 ffmpeg openexr tbb libgdiplus
+ pacman -S libpng openjpeg2 libjpeg-turbo libgeotiff tbb libgdiplus
elif testcmd dnf; then
osVariant="rhel"
dnf update -y
- dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
- dnf install -y https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
- dnf install -y libjpeg-devel libjpeg-turbo-devel libpng-devel libgeotiff-devel libdc1394-devel ffmpeg-devel tbb-devel mesa-libGL libgdiplus
+ #dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
+ #dnf install -y https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
+ #dnf install -y libjpeg-devel libjpeg-turbo-devel libpng-devel libgeotiff-devel libdc1394-devel ffmpeg-devel tbb-devel mesa-libGL libgdiplus
+ dnf install -y libjpeg-devel libjpeg-turbo-devel libpng-devel libgeotiff-devel tbb-devel libgdiplus
else
echo "Error: Base operative system / package manager not identified, nothing was installed"
exit -1
diff --git a/Scripts/install-uvtools.sh b/Scripts/install-uvtools.sh
index 8c07c09..d8332b2 100644
--- a/Scripts/install-uvtools.sh
+++ b/Scripts/install-uvtools.sh
@@ -195,7 +195,7 @@ fi
#############
echo "- Detected: $osVariant $arch"
-requiredlddversion="2.32"
+requiredlddversion="2.31"
lddversion="$(ldd --version | awk '/ldd/{print $NF}')"
@@ -205,16 +205,19 @@ if [ $(version $lddversion) -lt $(version $requiredlddversion) ]; then
echo "Error: Unable to auto install the latest version."
echo "ldd version: $lddversion detected, but requires at least version $requiredlddversion."
echo "Solutions:"
- echo "- Manually download and run older version: https://github.com/sn4k3/UVtools/releases/tag/v3.7.2"
echo "- Upgrade your system to the most recent version"
echo "- Try to upgrade glibc to at least $requiredlddversion (Search about this as it can break your system)"
echo "##########################################################"
exit -1
fi
-if [ -z "$(ldconfig -p | grep libpng)" -o -z "$(ldconfig -p | grep libgdiplus)" -o -z "$(ldconfig -p | grep libgeotiff)" -o -z "$(ldconfig -p | grep libavcodec)" ]; then
- echo "- Missing dependencies found, installing..."
- sudo bash -c "$(curl -fsSL $dependencies_url)"
+if testcmd ldconfig; then
+ if [ -z "$(ldconfig -p | grep libpng)" -o -z "$(ldconfig -p | grep libgdiplus)" -o -z "$(ldconfig -p | grep libgeotiff)" ]; then
+ echo "- Missing dependencies found, installing..."
+ sudo bash -c "$(curl -fsSL $dependencies_url)"
+ fi
+else
+ echo "Unable to detect for missing dependencies, ldconfig not found, however installation will continue."
fi
echo "- Detecting download"
diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs
index a098f49..7568732 100644
--- a/UVtools.Cmd/Program.cs
+++ b/UVtools.Cmd/Program.cs
@@ -33,19 +33,21 @@ internal class Program
public static async Task<int> Main(params string[] args)
{
- Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Args = args;
var rootCommand = new RootCommand("MSLA/DLP, file analysis, repair, conversion and manipulation")
{
+ SetPropertiesCommand.CreateCommand(),
RunCommand.CreateCommand(),
ConvertCommand.CreateCommand(),
ExtractCommand.CreateCommand(),
CopyParametersCommand.CreateCommand(),
SetThumbnailCommand.CreateCommand(),
+ PrintIssuesCommand.CreateCommand(),
PrintPropertiesCommand.CreateCommand(),
- PrintLayersCommand.CreateCommand(),
+ //PrintLayersCommand.CreateCommand(),
PrintGCodeCommand.CreateCommand(),
PrintMachinesCommand.CreateCommand(),
diff --git a/UVtools.Cmd/ReflectionPropertyValue.cs b/UVtools.Cmd/ReflectionPropertyValue.cs
new file mode 100644
index 0000000..a5b1964
--- /dev/null
+++ b/UVtools.Cmd/ReflectionPropertyValue.cs
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+namespace UVtools.Cmd
+{
+ internal sealed class ReflectionPropertyValue
+ {
+ public string Name { get; init; }
+ public string Value { get; init; }
+ public bool Found { get; set; }
+
+ public ReflectionPropertyValue(string name, string value)
+ {
+ Name = name;
+ Value = value;
+ }
+
+ public void Deconstruct(out string name, out string value)
+ {
+ name = Name;
+ value = Value;
+ }
+
+ public void SetFound() => Found = true;
+
+ private bool Equals(ReflectionPropertyValue other)
+ {
+ return Name == other.Name && Value == other.Value;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is ReflectionPropertyValue other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Name, Value);
+ }
+
+ public override string ToString()
+ {
+ return $"{Name}={Value}";
+ }
+ }
+}
diff --git a/UVtools.Cmd/Symbols/GlobalOptions.cs b/UVtools.Cmd/Symbols/GlobalOptions.cs
index aca565a..c3d044a 100644
--- a/UVtools.Cmd/Symbols/GlobalOptions.cs
+++ b/UVtools.Cmd/Symbols/GlobalOptions.cs
@@ -15,7 +15,7 @@ internal static class GlobalOptions
{
internal static Option<bool> QuietOption { get; } = new(new[] { "-q", "--quiet" }, "Make output silent but exceptions error will still show");
internal static Option<bool> NoProgressOption { get; } = new(new[] { "--no-progress" }, "Show no progress");
- internal static Option<FileInfo?> OutputFile { get; } = new(new[] { "-o", "--output" }, "Output file to save");
+ internal static Option<FileInfo> OutputFile { get; } = new(new[] { "-o", "--output" }, "Output file to save");
internal static Option<bool> OpenInPartialMode { get; } = new(new []{ "--partial-mode"}, "Fast load the file in partial mode");
} \ No newline at end of file
diff --git a/UVtools.Cmd/Symbols/PrintGCodeCommand.cs b/UVtools.Cmd/Symbols/PrintGCodeCommand.cs
index 3dcf604..65c95c1 100644
--- a/UVtools.Cmd/Symbols/PrintGCodeCommand.cs
+++ b/UVtools.Cmd/Symbols/PrintGCodeCommand.cs
@@ -8,7 +8,6 @@
using System;
using System.CommandLine;
-using System.IO;
using UVtools.Core.FileFormats;
namespace UVtools.Cmd.Symbols;
diff --git a/UVtools.Cmd/Symbols/PrintIssuesCommand.cs b/UVtools.Cmd/Symbols/PrintIssuesCommand.cs
new file mode 100644
index 0000000..5d9fcb9
--- /dev/null
+++ b/UVtools.Cmd/Symbols/PrintIssuesCommand.cs
@@ -0,0 +1,91 @@
+/*
+ * 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.CommandLine;
+using System.Linq;
+using UVtools.Core.Layers;
+
+namespace UVtools.Cmd.Symbols;
+
+internal static class PrintIssuesCommand
+{
+ internal static Command CreateCommand()
+ {
+ var islandsOption = new Option<bool>(new []{ "-i" , "--islands"}, "Enable islands detection");
+ var overhangsOption = new Option<bool>(new []{ "-o" , "--overhangs"}, "Enable overhangs detection");
+ var resinTrapOption = new Option<bool>(new []{ "-r" , "--resin-traps"}, "Enable resin-traps detection");
+ var suctionCupOption = new Option<bool>(new []{ "-s" , "--suction-cups"}, "Enable suction-cups detection");
+ var touchingBoundsOption = new Option<bool>(new []{ "-t" , "--touching-bounds"}, "Enable touching bounds detection");
+ var printHeightOption = new Option<bool>(new []{ "-p" , "--print-height"}, "Enable print height detection");
+ var emptyLayersOption = new Option<bool>(new []{ "-e" , "--empty-layers"}, "Enable empty layer detection");
+
+ var sortByAreaOption = new Option<bool>(new []{ "--sort-area"}, "Sort by area DESC");
+
+ var command = new Command("print-issues", "Detect and print issues")
+ {
+ GlobalArguments.InputFileArgument,
+
+ islandsOption,
+ overhangsOption,
+ resinTrapOption,
+ suctionCupOption,
+ touchingBoundsOption,
+ printHeightOption,
+ emptyLayersOption,
+
+ sortByAreaOption
+ };
+
+ command.SetHandler(context =>
+ {
+ var inputFile = context.ParseResult.GetValueForArgument(GlobalArguments.InputFileArgument);
+ var islands = context.ParseResult.GetValueForOption(islandsOption);
+ var overhangs = context.ParseResult.GetValueForOption(overhangsOption);
+ var resinTraps = context.ParseResult.GetValueForOption(resinTrapOption);
+ var suctionCups = context.ParseResult.GetValueForOption(suctionCupOption);
+ var touchingBounds = context.ParseResult.GetValueForOption(touchingBoundsOption);
+ var printHeight = context.ParseResult.GetValueForOption(printHeightOption);
+ var emptyLayers = context.ParseResult.GetValueForOption(emptyLayersOption);
+ var sortByArea = context.ParseResult.GetValueForOption(sortByAreaOption);
+
+ var slicerFile = Program.OpenInputFile(inputFile);
+
+ var config = new IssuesDetectionConfiguration();
+
+ if (islands || overhangs || resinTraps || suctionCups || touchingBounds || printHeight || emptyLayers)
+ {
+ config.DisableAll();
+ config.IslandConfig.Enabled = islands;
+ config.OverhangConfig.Enabled = overhangs;
+ config.ResinTrapConfig.Enabled = resinTraps;
+ config.ResinTrapConfig.DetectSuctionCups = suctionCups;
+ config.TouchingBoundConfig.Enabled = touchingBounds;
+ config.PrintHeightConfig.Enabled = printHeight;
+ config.EmptyLayerConfig.Enabled = emptyLayers;
+ }
+
+ var issues = Program.ProgressBarWork("Detecting issues", () => slicerFile.IssueManager.DetectIssues(config, Program.Progress));
+ if (sortByArea)
+ {
+ issues = issues.OrderBy(issue => issue.Type)
+ .ThenByDescending(issue => issue.Area)
+ .ThenBy(issue => issue.StartLayerIndex).ToList();
+ }
+
+ Console.WriteLine($"Issues: {issues.Count}");
+
+ foreach (var issue in issues)
+ {
+ Console.WriteLine($"{issue.Type}, {issue.LayerInfoStr}, {issue.Area:F0}px{issue.AreaChar}, {issue.BoundingRectangle}");
+ }
+ });
+
+ return command;
+ }
+} \ No newline at end of file
diff --git a/UVtools.Cmd/Symbols/PrintLayersCommand.cs b/UVtools.Cmd/Symbols/PrintLayersCommand.cs
index a988e5a..bb11eb0 100644
--- a/UVtools.Cmd/Symbols/PrintLayersCommand.cs
+++ b/UVtools.Cmd/Symbols/PrintLayersCommand.cs
@@ -12,7 +12,6 @@ using System.CommandLine;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
-using System.Text.RegularExpressions;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
@@ -22,9 +21,18 @@ internal static class PrintLayersCommand
{
internal static Command CreateCommand()
{
- var rangeOption = new Option<string>(new[] { "-r", "--range" }, "Prints only the matching layer index(es) in a range");
- var indexesOption = new Option<ushort[]>(new[] {"-i", "--indexes"}, "Prints only the matching layer index(es)");
- var matchNamesOption = new Option<string[]>(new[] { "-n", "--names" }, "Prints only the name matching properties");
+ var rangeOption = new Option<string>(new[] { "-r", "--range" }, "Prints only the matching layer(s) index(es) in a range")
+ {
+ ArgumentHelpName = "startindex:endindex"
+ };
+ var indexesOption = new Option<uint[]>(new[] {"-i", "--indexes"}, "Prints only the matching layer(s) index(es)")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
+ var matchNamesOption = new Option<string[]>(new[] { "-n", "--names" }, "Prints only the name matching properties")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
var command = new Command("print-layers", "Prints layer(s) properties")
{
@@ -38,43 +46,33 @@ internal static class PrintLayersCommand
command.SetHandler((inputFile, layerRange, layerIndexes, matchNames, partialMode) =>
{
var slicerFile = Program.OpenInputFile(inputFile, partialMode ? FileFormat.FileDecodeType.Partial : FileFormat.FileDecodeType.Full);
-
var layerIndexesList = new List<uint>();
if (!string.IsNullOrWhiteSpace(layerRange))
{
- var match = Regex.Match(layerRange, @"(\d+)(:|\||-)(\d+)");
- if (match.Success && match.Groups.Count >= 4)
+ if (slicerFile.TryParseLayerIndexRange(layerRange, out var layerIndexStart, out var layerIndexEnd))
{
- var startNumberStr = match.Groups[1].Value;
- var endNumberStr = match.Groups[3].Value;
-
- if (uint.TryParse(startNumberStr, out var startLayerIndex) &&
- uint.TryParse(endNumberStr, out var endLayerIndex))
+ for (var layerIndex = layerIndexStart; layerIndex <= layerIndexEnd; layerIndex++)
{
- if (startLayerIndex > endLayerIndex)
- {
- (startLayerIndex, endLayerIndex) = (endLayerIndex, startLayerIndex);
- }
-
- for (var layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++)
- {
- layerIndexesList.Add(layerIndex);
- }
+ layerIndexesList.Add(layerIndex);
}
}
+ else
+ {
+ Program.WriteLineError($"The specified layer range '{layerRange}' is malformed, use startindex:endindex with positive numbers");
+ }
}
if (layerIndexes.Length == 0 && layerIndexesList.Count == 0)
{
for (uint i = 0; i < slicerFile.LayerCount; i++)
{
- layerIndexesList.Add(i);
+ layerIndexesList.Add(slicerFile.SanitizeLayerIndex(i));
}
}
else
{
- layerIndexesList.AddRange(layerIndexes.Select(layerIndex => (uint) layerIndex));
+ layerIndexesList.AddRange(layerIndexes.Select(layerIndex => slicerFile.SanitizeLayerIndex(layerIndex)));
}
layerIndexesList = layerIndexesList.Distinct().OrderBy(layerIndex => layerIndex).ToList();
@@ -82,8 +80,7 @@ internal static class PrintLayersCommand
foreach (var layerIndex in layerIndexesList)
{
Console.WriteLine($"Layer: {layerIndex}");
- foreach (var propertyInfo in slicerFile[layerIndex].GetType()
- .GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ foreach (var propertyInfo in slicerFile[layerIndex].GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (propertyInfo.Name.Equals("Item")) continue;
if (matchNames is not null && matchNames.Length > 0)
diff --git a/UVtools.Cmd/Symbols/PrintMachines.cs b/UVtools.Cmd/Symbols/PrintMachines.cs
index efa3353..e0fd460 100644
--- a/UVtools.Cmd/Symbols/PrintMachines.cs
+++ b/UVtools.Cmd/Symbols/PrintMachines.cs
@@ -8,10 +8,8 @@
using System;
using System.CommandLine;
-using System.IO;
using System.Text.Json;
using UVtools.Core.Extensions;
-using UVtools.Core.FileFormats;
using UVtools.Core.Printer;
namespace UVtools.Cmd.Symbols;
diff --git a/UVtools.Cmd/Symbols/PrintPropertiesCommand.cs b/UVtools.Cmd/Symbols/PrintPropertiesCommand.cs
index 622cd0a..a4afc86 100644
--- a/UVtools.Cmd/Symbols/PrintPropertiesCommand.cs
+++ b/UVtools.Cmd/Symbols/PrintPropertiesCommand.cs
@@ -7,8 +7,8 @@
*/
using System;
+using System.Collections.Generic;
using System.CommandLine;
-using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
@@ -21,39 +21,128 @@ internal static class PrintPropertiesCommand
{
internal static Command CreateCommand()
{
- var matchNamesOption = new Option<string[]>(new[] {"-n", "--names"}, "Prints only the name matching properties");
- var showPropertiesOnlyOption = new Option<bool>(new[] { "-b", "--base" }, "Prints only the base properties of the file");
+ var matchNamesOption = new Option<string[]>(new[] {"-n", "--names"}, "Prints only the name matching properties")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
+ var allPropertiesOption = new Option<bool>(new[] { "-a", "--all" }, "Also prints the sub properties of the file (No effect on layers)");
+ var layerRangeOption = new Option<string>(new[] { "-r", "--range" }, "Prints only the matching layer(s) index(es) in a range")
+ {
+ ArgumentHelpName = "startindex:endindex"
+ };
+ var layerIndexesOption = new Option<uint[]>(new[] { "-i", "--indexes" }, "Prints only the matching layer(s) index(es)")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
var command = new Command("print-properties", "Prints available properties")
{
GlobalArguments.InputFileArgument,
matchNamesOption,
- showPropertiesOnlyOption,
+ allPropertiesOption,
+ layerRangeOption,
+ layerIndexesOption,
GlobalOptions.OpenInPartialMode
};
- command.SetHandler((inputFile, matchNames, baseOnly, partialMode) =>
+ command.SetHandler((inputFile, matchNames, allProperties, layerRange, layerIndexes, partialMode) =>
{
var slicerFile = Program.OpenInputFile(inputFile, partialMode ? FileFormat.FileDecodeType.Partial : FileFormat.FileDecodeType.Full);
uint count = 0;
- Console.WriteLine("Listing properties:");
- Console.WriteLine("----------------------");
- if (!baseOnly)
+ var layerIndexesList = new List<uint>();
+
+ if (!string.IsNullOrWhiteSpace(layerRange))
+ {
+ if (slicerFile.TryParseLayerIndexRange(layerRange, out var layerIndexStart, out var layerIndexEnd))
+ {
+ for (var layerIndex = layerIndexStart; layerIndex <= layerIndexEnd; layerIndex++)
+ {
+ layerIndexesList.Add(layerIndex);
+ }
+ }
+ else
+ {
+ Program.WriteLineError($"The specified layer range '{layerRange}' is malformed, use startindex:endindex with positive numbers");
+ }
+ }
+
+ if (layerIndexes.Length > 0)
+ {
+ layerIndexesList.AddRange(layerIndexes.Select(layerIndex => slicerFile.SanitizeLayerIndex(layerIndex)));
+ }
+
+ layerIndexesList = layerIndexesList.Distinct().OrderBy(layerIndex => layerIndex).ToList();
+ Console.WriteLine("-------------------------");
+
+ if (layerIndexesList.Count == 0)
{
- foreach (var config in slicerFile.Configs)
+ if (allProperties)
{
- //Program.WriteLine("******************************");
- //Program.WriteLine($"\t{config.GetType().Name}");
- //Program.WriteLine("******************************");
- foreach (var propertyInfo in config.GetType()
+ foreach (var config in slicerFile.Configs)
+ {
+ //Program.WriteLine("******************************");
+ //Program.WriteLine($"\t{config.GetType().Name}");
+ //Program.WriteLine("******************************");
+ foreach (var propertyInfo in config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ if (propertyInfo.Name.Equals("Item")) continue;
+ if (matchNames.Length > 0)
+ {
+ if(matchNames.All(s => s != propertyInfo.Name)) continue;
+ }
+ if (propertyInfo.GetCustomAttributes().Any(attribute =>
+ {
+ var type = attribute.GetType();
+ if (type == typeof(XmlIgnoreAttribute)) return true;
+ if (type == typeof(JsonIgnoreAttribute)) return true;
+ return false;
+ })) continue;
+ count++;
+ Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(config)}");
+ }
+ }
+ }
+
+ //Program.WriteLine("******************************");
+ //Program.WriteLine("\tBase");
+ //Program.WriteLine("******************************");
+
+
+ foreach (var propertyInfo in slicerFile.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ if (propertyInfo.Name.Equals("Item")) continue;
+ if (matchNames is not null && matchNames.Length > 0)
+ {
+ if (matchNames.All(s => s != propertyInfo.Name)) continue;
+ }
+ if (propertyInfo.GetCustomAttributes().Any(attribute =>
+ {
+ var type = attribute.GetType();
+ if (type == typeof(XmlIgnoreAttribute)) return true;
+ if (type == typeof(JsonIgnoreAttribute)) return true;
+ return false;
+ })) continue;
+ count++;
+ Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(slicerFile)}");
+ }
+ }
+ else
+ {
+ for (var i = 0; i < layerIndexesList.Count; i++)
+ {
+ var layerIndex = layerIndexesList[i];
+ if(i > 0) Console.WriteLine("-------------------------");
+ Console.WriteLine($"# Layer: {layerIndex}");
+ foreach (var propertyInfo in slicerFile[layerIndex].GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (propertyInfo.Name.Equals("Item")) continue;
- if (matchNames.Length > 0)
+ if (matchNames is not null && matchNames.Length > 0)
{
- if(matchNames.All(s => s != propertyInfo.Name)) continue;
+ if (matchNames.All(s => s != propertyInfo.Name)) continue;
}
+
if (propertyInfo.GetCustomAttributes().Any(attribute =>
{
var type = attribute.GetType();
@@ -61,42 +150,16 @@ internal static class PrintPropertiesCommand
if (type == typeof(JsonIgnoreAttribute)) return true;
return false;
})) continue;
+ Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(slicerFile[layerIndex])}");
count++;
- Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(config)}");
}
}
}
- //Program.WriteLine("******************************");
- //Program.WriteLine("\tBase");
- //Program.WriteLine("******************************");
-
- var fileFormat = slicerFile as FileFormat;
-
- foreach (var propertyInfo in fileFormat.GetType()
- .GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- if (propertyInfo.Name.Equals("Item")) continue;
- if (matchNames is not null && matchNames.Length > 0)
- {
- if (matchNames.All(s => s != propertyInfo.Name)) continue;
- }
- if (propertyInfo.GetCustomAttributes().Any(attribute =>
- {
- var type = attribute.GetType();
- if (type == typeof(XmlIgnoreAttribute)) return true;
- if (type == typeof(JsonIgnoreAttribute)) return true;
- return false;
- })) continue;
- count++;
- Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(fileFormat)}");
- }
-
-
- Console.WriteLine("----------------------");
+ Console.WriteLine("-------------------------");
Console.WriteLine($"Total properties: {count}");
- }, GlobalArguments.InputFileArgument, matchNamesOption, showPropertiesOnlyOption, GlobalOptions.OpenInPartialMode);
+ }, GlobalArguments.InputFileArgument, matchNamesOption, allPropertiesOption, layerRangeOption, layerIndexesOption, GlobalOptions.OpenInPartialMode);
return command;
}
diff --git a/UVtools.Cmd/Symbols/RunCommand.cs b/UVtools.Cmd/Symbols/RunCommand.cs
index 8a7597a..bcaf156f 100644
--- a/UVtools.Cmd/Symbols/RunCommand.cs
+++ b/UVtools.Cmd/Symbols/RunCommand.cs
@@ -6,9 +6,12 @@
* of this license document, but changing it is not allowed.
*/
+using System;
+using System.Collections.Generic;
using System.CommandLine;
using System.IO;
-using System.Linq;
+using System.Reflection;
+using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
@@ -19,28 +22,42 @@ internal static class RunCommand
internal static Command CreateCommand()
{
var filesArgument = new Argument<FileInfo[]>("files", "Operation and script files to run (.uvtop, .cs, .csx)").ExistingOnly();
+ var propertiesOption = new Option<string[]>(new []{ "-p" , "--property"}, "Set a property with a new value (Compatible with operations only)")
+ {
+ AllowMultipleArgumentsPerToken = true,
+ ArgumentHelpName = "property=value"
+ };
var command = new Command("run", "Run operations and/or scripts")
{
GlobalArguments.InputFileArgument,
filesArgument,
+ propertiesOption,
GlobalOptions.OutputFile,
GlobalOptions.OpenInPartialMode
};
- command.SetHandler((inputFile, files, outputFile, partialMode) =>
+ command.SetHandler((inputFile, files, properties, outputFile, partialMode) =>
{
if (files.Length == 0)
{
- Program.WriteLineError("No files to run");
+ Program.WriteLineError("No specified files to run");
return;
}
var slicerFile = Program.OpenInputFile(inputFile, partialMode ? FileFormat.FileDecodeType.Partial : FileFormat.FileDecodeType.Full);
uint runs = 0;
- uint sucessfullRuns = 0;
+ uint successfulRuns = 0;
+
+ var parsedProperties = new List<ReflectionPropertyValue>(properties.Length);
+ foreach (var property in properties)
+ {
+ var split = property.Split(new[] {'=', ':'}, 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ if(split.Length < 2) continue;
+ parsedProperties.Add(new (split[0], split[1]));
+ }
foreach (var file in files)
{
@@ -48,6 +65,19 @@ internal static class RunCommand
if (operation is not null)
{
+ if (parsedProperties.Count > 0)
+ {
+ foreach (var propertyInfo in operation.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ foreach (var property in parsedProperties)
+ {
+ if (propertyInfo.Name != property.Name) continue;
+ propertyInfo.SetValueFromString(operation, property.Value);
+ property.Found = true;
+ }
+ }
+ }
+
operation.SlicerFile = slicerFile;
var result = operation.ValidateInternally();
if (string.IsNullOrWhiteSpace(result))
@@ -55,7 +85,7 @@ internal static class RunCommand
Program.ProgressBarWork($"Operation {++runs}: {operation.ProgressTitle}",
() =>
{
- if(operation.Execute(Program.Progress)) sucessfullRuns++;
+ if(operation.Execute(Program.Progress)) successfulRuns++;
});
}
else
@@ -76,7 +106,7 @@ internal static class RunCommand
Program.ProgressBarWork($"Script {++runs}: {operationScripting.ScriptGlobals?.Script.Name ?? operationScripting.ProgressTitle}",
() =>
{
- if (operationScripting.Execute(Program.Progress)) sucessfullRuns++;
+ if (operationScripting.Execute(Program.Progress)) successfulRuns++;
});
}
else
@@ -89,8 +119,14 @@ internal static class RunCommand
Program.WriteLineWarning($"Invalid file: {file.Name}");
}
- if(sucessfullRuns > 0) Program.SaveFile(slicerFile, outputFile);
- }, GlobalArguments.InputFileArgument, filesArgument, GlobalOptions.OutputFile, GlobalOptions.OpenInPartialMode);
+ foreach (var property in parsedProperties)
+ {
+ if (property.Found) continue;
+ Program.WriteLineWarning($"Property {property.Name} was defined but not found nor set.");
+ }
+
+ if(successfulRuns > 0) Program.SaveFile(slicerFile, outputFile);
+ }, GlobalArguments.InputFileArgument, filesArgument, propertiesOption, GlobalOptions.OutputFile, GlobalOptions.OpenInPartialMode);
return command;
}
diff --git a/UVtools.Cmd/Symbols/SetPropertiesCommand.cs b/UVtools.Cmd/Symbols/SetPropertiesCommand.cs
new file mode 100644
index 0000000..c9dadee
--- /dev/null
+++ b/UVtools.Cmd/Symbols/SetPropertiesCommand.cs
@@ -0,0 +1,134 @@
+/*
+ * 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.Collections.Generic;
+using System.CommandLine;
+using System.Linq;
+using System.Reflection;
+using UVtools.Core.Extensions;
+using UVtools.Core.FileFormats;
+
+namespace UVtools.Cmd.Symbols;
+
+internal static class SetPropertiesCommand
+{
+ internal static Command CreateCommand()
+ {
+ var propertiesArgument = new Argument<string[]>("property=value", "Properties names and values to set")
+ {
+ Arity = ArgumentArity.OneOrMore
+ };
+ var layerRangeOption = new Option<string>(new[] { "-r", "--range" }, "Sets properties to the matching layer(s) index(es) in a range")
+ {
+ ArgumentHelpName = "startindex:endindex"
+ };
+ var layerIndexesOption = new Option<uint[]>(new[] { "-i", "--indexes" }, "Sets properties to the matching layer(s) index(es)")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
+
+ var command = new Command("set-properties", "Set properties in a file or to it layers with new values")
+ {
+ GlobalArguments.InputFileArgument,
+ propertiesArgument,
+
+ layerRangeOption,
+ layerIndexesOption,
+ GlobalOptions.OutputFile,
+ };
+
+ command.SetHandler((inputFile, properties, layerRange, layerIndexes, outputFile) =>
+ {
+ var parsedProperties = new List<ReflectionPropertyValue>(properties.Length);
+
+ foreach (var property in properties)
+ {
+ var split = property.Split(new[] { '=', ':' }, 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ if (split.Length < 2) continue;
+ parsedProperties.Add(new(split[0], split[1]));
+ }
+
+ if (parsedProperties.Count == 0)
+ {
+ Program.WriteLineError("No properties to set.");
+ }
+
+ var slicerFile = Program.OpenInputFile(inputFile, FileFormat.FileDecodeType.Partial);
+
+ var layerIndexesList = new List<uint>();
+
+ if (!string.IsNullOrWhiteSpace(layerRange))
+ {
+ if (slicerFile.TryParseLayerIndexRange(layerRange, out var layerIndexStart, out var layerIndexEnd))
+ {
+ for (var layerIndex = layerIndexStart; layerIndex <= layerIndexEnd; layerIndex++)
+ {
+ layerIndexesList.Add(layerIndex);
+ }
+ }
+ else
+ {
+ Program.WriteLineError($"The specified layer range '{layerRange}' is malformed, use startindex:endindex with positive numbers");
+ }
+ }
+
+ if (layerIndexes.Length > 0)
+ {
+ layerIndexesList.AddRange(layerIndexes.Select(layerIndex => slicerFile.SanitizeLayerIndex(layerIndex)));
+ }
+
+ layerIndexesList = layerIndexesList.Distinct().OrderBy(layerIndex => layerIndex).ToList();
+ uint setProperties = 0;
+
+ if (layerIndexesList.Count == 0)
+ {
+ foreach (var propertyInfo in slicerFile.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ foreach (var property in parsedProperties)
+ {
+ if (propertyInfo.Name != property.Name) continue;
+ propertyInfo.SetValueFromString(slicerFile, property.Value);
+ property.Found = true;
+ setProperties++;
+ }
+ }
+ }
+ else
+ {
+ foreach (var layerIndex in layerIndexesList)
+ {
+ var layer = slicerFile[layerIndex];
+ foreach (var propertyInfo in layer.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ foreach (var property in parsedProperties)
+ {
+ if (propertyInfo.Name != property.Name) continue;
+ propertyInfo.SetValueFromString(layer, property.Value);
+ property.Found = true;
+ setProperties++;
+ }
+ }
+ }
+ }
+
+
+ foreach (var property in parsedProperties)
+ {
+ if (property.Found) continue;
+ Program.WriteLineWarning($"Property {property.Name} was defined but not found nor set.");
+ }
+
+ if (setProperties <= 0) return;
+ Program.WriteLine($"Properties set: {setProperties}");
+ Program.SaveFile(slicerFile, outputFile);
+ }, GlobalArguments.InputFileArgument, propertiesArgument, layerRangeOption, layerIndexesOption, GlobalOptions.OutputFile);
+
+ return command;
+ }
+} \ No newline at end of file
diff --git a/UVtools.Cmd/Symbols/SetThumbnailCommand.cs b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs
index 9143ada..718778d 100644
--- a/UVtools.Cmd/Symbols/SetThumbnailCommand.cs
+++ b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs
@@ -8,10 +8,8 @@
using Emgu.CV;
using System;
-using System.Collections.Generic;
using System.CommandLine;
using System.IO;
-using System.Linq;
using Emgu.CV.CvEnum;
namespace UVtools.Cmd.Symbols;
@@ -23,7 +21,10 @@ internal static class SetThumbnailCommand
internal static Command CreateCommand()
{
var sourceArgument = new Argument<string>($"file path|layer index|{RandomLayerArg}|{HeatmapArg}", () => HeatmapArg, "Choose from a file, layer index, random layer or generate a heatmap");
- var thumbnailIndexesOption = new Option<IEnumerable<byte>>("-i", "Select the thumbnail index(es) to set");
+ var thumbnailIndexesOption = new Option<byte[]>(new []{"-i", "--indexes" }, "Prints only the matching thumbnail(s) index(es)")
+ {
+ AllowMultipleArgumentsPerToken = true
+ };
var command = new Command("set-thumbnail", "Sets and replace thumbnail(s) in the file")
{
@@ -52,7 +53,7 @@ internal static class SetThumbnailCommand
CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr);
- if (thumbnailIndexes.Any())
+ if (thumbnailIndexes.Length > 0)
{
foreach (var thumbnailIndex in thumbnailIndexes)
{
@@ -77,7 +78,7 @@ internal static class SetThumbnailCommand
using var matRoi = slicerFile[Random.Shared.Next((int) slicerFile.LayerCount)].GetLayerMatBoundingRectangle(50, 100);
CvInvoke.CvtColor(matRoi.RoiMat, matRoi.RoiMat, ColorConversion.Gray2Bgr);
- if (thumbnailIndexes.Any())
+ if (thumbnailIndexes.Length > 0)
{
foreach (var thumbnailIndex in thumbnailIndexes)
{
@@ -102,7 +103,7 @@ internal static class SetThumbnailCommand
using var matRoi = slicerFile[layerIndex].GetLayerMatBoundingRectangle(50, 100);
CvInvoke.CvtColor(matRoi.RoiMat, matRoi.RoiMat, ColorConversion.Gray2Bgr);
- if (thumbnailIndexes.Any())
+ if (thumbnailIndexes.Length > 0)
{
foreach (var thumbnailIndex in thumbnailIndexes)
{
@@ -122,7 +123,7 @@ internal static class SetThumbnailCommand
{
var slicerFile = Program.OpenInputFile(inputFile);
- if (thumbnailIndexes.Any())
+ if (thumbnailIndexes.Length > 0)
{
foreach (var thumbnailIndex in thumbnailIndexes)
{
diff --git a/UVtools.Cmd/UVtools.Cmd.csproj b/UVtools.Cmd/UVtools.Cmd.csproj
index ecbec54..44b2f18 100644
--- a/UVtools.Cmd/UVtools.Cmd.csproj
+++ b/UVtools.Cmd/UVtools.Cmd.csproj
@@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>UVtoolsCmd</AssemblyName>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
- <Version>1.0.4</Version>
+ <Version>1.0.5</Version>
<Authors>Tiago Conceição, sn4k3</Authors>
<Company>PTRTECH</Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
diff --git a/UVtools.Core/Extensions/ReflectionExtensions.cs b/UVtools.Core/Extensions/ReflectionExtensions.cs
new file mode 100644
index 0000000..060fb08
--- /dev/null
+++ b/UVtools.Core/Extensions/ReflectionExtensions.cs
@@ -0,0 +1,125 @@
+/*
+ * 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.Globalization;
+using System.Reflection;
+
+namespace UVtools.Core.Extensions;
+
+public static class ReflectionExtensions
+{
+ public static bool SetValueFromString(this PropertyInfo attribute, object obj, string value)
+ {
+ if (attribute.PropertyType == typeof(string))
+ {
+ attribute.SetValue(obj, value.Convert<string>());
+ return true;
+ }
+
+ if (string.IsNullOrEmpty(value)) return false;
+
+ if (attribute.PropertyType.IsEnum)
+ {
+ if (Enum.TryParse(attribute.PropertyType, value, true, out var enumValue))
+ {
+ attribute.SetValue(obj, Enum.Parse(attribute.PropertyType, enumValue?.ToString() ?? string.Empty));
+ return true;
+ }
+
+ throw new ArgumentException($"The requested enum name '{value}' was not found.\nAvailable names: ({string.Join(", ", Enum.GetNames(attribute.PropertyType))}).");
+ }
+
+ if (attribute.PropertyType == typeof(bool))
+ {
+ //if (value == "!") attribute.SetValue(obj, !bool.Parse(attribute.GetValue(obj).ToString()));
+ if (value.Length == 1 && char.IsDigit(value[0])) attribute.SetValue(obj, value[0] != '0');
+ else attribute.SetValue(obj, value.Equals("true", StringComparison.OrdinalIgnoreCase));
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(byte))
+ {
+ if (value.Equals("true", StringComparison.OrdinalIgnoreCase)) attribute.SetValue(obj, (byte)1);
+ else if (value.Equals("false", StringComparison.OrdinalIgnoreCase)) attribute.SetValue(obj, byte.MinValue);
+ else attribute.SetValue(obj, value.Convert<byte>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(sbyte))
+ {
+ attribute.SetValue(obj, value.Convert<sbyte>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(ushort))
+ {
+ attribute.SetValue(obj, value.Convert<ushort>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(short))
+ {
+ attribute.SetValue(obj, value.Convert<short>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(uint))
+ {
+ attribute.SetValue(obj, value.Convert<uint>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(int))
+ {
+ attribute.SetValue(obj, value.Convert<int>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(ulong))
+ {
+ attribute.SetValue(obj, value.Convert<ulong>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(long))
+ {
+ attribute.SetValue(obj, value.Convert<long>());
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(Half))
+ {
+ attribute.SetValue(obj, Half.Parse(value, CultureInfo.InvariantCulture.NumberFormat));
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(float))
+ {
+ attribute.SetValue(obj, float.Parse(value, CultureInfo.InvariantCulture.NumberFormat));
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(double))
+ {
+ attribute.SetValue(obj, double.Parse(value, CultureInfo.InvariantCulture.NumberFormat));
+ return true;
+ }
+
+ if (attribute.PropertyType == typeof(decimal))
+ {
+ attribute.SetValue(obj, decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat));
+ return true;
+ }
+
+ throw new Exception($"Data type '{attribute.PropertyType.Name}' not recognized nor implemented.");
+ }
+
+ public static bool SetValueFromString(this PropertyInfo attribute, object obj, object? value) =>
+ attribute.SetValueFromString(obj, value?.ToString() ?? string.Empty);
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index b4b5e0e..e55bf48 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -29,7 +29,7 @@ namespace UVtools.Core.FileFormats;
#region Wanhao
-[Serializable]
+
[XmlRoot(ElementName = "manifest")]
public sealed class CWSManifest
{
@@ -70,7 +70,7 @@ public sealed class CWSManifest
public Slice GCode { get; set; } = new();
}
-[Serializable]
+
[XmlRoot(ElementName = "SliceBuildConfig")]
public sealed class CWSSliceBuildConfig
{
@@ -755,7 +755,7 @@ public class CWSFile : FileFormat
var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
if (displayNameAttribute is null) continue;
if (!splitLine[0].Trim().Equals(displayNameAttribute.DisplayName)) continue;
- Helpers.SetPropertyValue(propertyInfo, SliceSettings, splitLine[1].Trim());
+ propertyInfo.SetValueFromString(SliceSettings, splitLine[1].Trim());
}
}
tr.Close();
@@ -792,7 +792,7 @@ public class CWSFile : FileFormat
if (!splitLine[0].Trim(' ', ';', '(').Equals(displayNameAttribute.DisplayName)) continue;
try
{
- Helpers.SetPropertyValue(propertyInfo, OutputSettings, splitLine[1].Trim(' ', ')', 'p', 'x', 'm', 'n', 's', '/'));
+ propertyInfo.SetValueFromString(OutputSettings, splitLine[1].Trim(' ', ')', 'p', 'x', 'm', 'n', 's', '/'));
}
catch
{
diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs
index c6d0448..e6fe714 100644
--- a/UVtools.Core/FileFormats/ChituboxZipFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs
@@ -461,7 +461,7 @@ public class ChituboxZipFile : FileFormat
var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
if (displayNameAttribute is null) continue;
if (!splitLine[0].Trim(' ', ';').Equals(displayNameAttribute.DisplayName)) continue;
- Helpers.SetPropertyValue(propertyInfo, HeaderSettings, splitLine[1].Trim());
+ propertyInfo.SetValueFromString(HeaderSettings, splitLine[1].Trim());
}
}
tr.Close();
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index b7e0eba..94e3d6b 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -5152,40 +5152,40 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
/// <summary>
/// Converts millimeters to pixels given the current resolution and display size
/// </summary>
- /// <param name="mm">Millimeters to convert</param>
+ /// <param name="millimeters">Millimeters to convert</param>
/// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
/// <returns>Pixels</returns>
- public uint MillimetersXToPixels(ushort mm, uint fallbackToPixels = 0)
+ public uint MillimetersXToPixels(float millimeters, uint fallbackToPixels = 0)
{
var ppmm = Xppmm;
if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
+ return (uint)(ppmm * millimeters);
}
/// <summary>
/// Converts millimeters to pixels given the current resolution and display size
/// </summary>
- /// <param name="mm">Millimeters to convert</param>
+ /// <param name="millimeters">Millimeters to convert</param>
/// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
/// <returns>Pixels</returns>
- public uint MillimetersYToPixels(ushort mm, uint fallbackToPixels = 0)
+ public uint MillimetersYToPixels(float millimeters, uint fallbackToPixels = 0)
{
var ppmm = Yppmm;
if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
+ return (uint)(ppmm * millimeters);
}
/// <summary>
/// Converts millimeters to pixels given the current resolution and display size
/// </summary>
- /// <param name="mm">Millimeters to convert</param>
+ /// <param name="millimeters">Millimeters to convert</param>
/// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
/// <returns>Pixels</returns>
- public uint MillimetersToPixels(ushort mm, uint fallbackToPixels = 0)
+ public uint MillimetersToPixels(float millimeters, uint fallbackToPixels = 0)
{
var ppmm = PpmmMax;
if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
+ return (uint)(ppmm * millimeters);
}
/// <summary>
@@ -5684,6 +5684,51 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
#region Layer methods
/// <summary>
+ /// Try to parse starting and ending layer index from a string
+ /// </summary>
+ /// <param name="value">String value to parse, in start:end format</param>
+ /// <param name="layerIndexStart">Parsed starting layer index</param>
+ /// <param name="layerIndexEnd">Parsed ending layer index</param>
+ /// <returns></returns>
+ public bool TryParseLayerIndexRange(string value, out uint layerIndexStart, out uint layerIndexEnd)
+ {
+ layerIndexStart = 0;
+ layerIndexEnd = LastLayerIndex;
+
+ if (string.IsNullOrWhiteSpace(value)) return false;
+
+ var split = value.Split(new[]{':', '|', '-'}, StringSplitOptions.TrimEntries);
+
+ if (split[0] != string.Empty)
+ {
+ if(split[0].Equals("FIRST", StringComparison.OrdinalIgnoreCase)) layerIndexStart = 0;
+ else if(split[0].Equals("LB", StringComparison.OrdinalIgnoreCase)) layerIndexStart = LastBottomLayer?.Index ?? 0;
+ else if(split[0].Equals("FN", StringComparison.OrdinalIgnoreCase)) layerIndexStart = FirstNormalLayer?.Index ?? 0;
+ else if(split[0].Equals("LAST", StringComparison.OrdinalIgnoreCase)) layerIndexStart = LastLayerIndex;
+ else if(!uint.TryParse(split[0], out layerIndexStart)) return false;
+ SanitizeLayerIndex(ref layerIndexStart);
+ }
+
+ if (split.Length == 1)
+ {
+ layerIndexEnd = layerIndexStart;
+ return true;
+ }
+
+ if (split[1] != string.Empty)
+ {
+ if (split[1].Equals("FIRST", StringComparison.OrdinalIgnoreCase)) layerIndexEnd = 0;
+ else if (split[1].Equals("LB", StringComparison.OrdinalIgnoreCase)) layerIndexEnd = LastBottomLayer?.Index ?? 0;
+ else if (split[1].Equals("FN", StringComparison.OrdinalIgnoreCase)) layerIndexEnd = FirstNormalLayer?.Index ?? 0;
+ else if (split[1].Equals("LAST", StringComparison.OrdinalIgnoreCase)) layerIndexEnd = LastLayerIndex;
+ else if (!uint.TryParse(split[1], out layerIndexEnd)) return false;
+ SanitizeLayerIndex(ref layerIndexEnd);
+ }
+
+ return layerIndexStart <= layerIndexEnd;
+ }
+
+ /// <summary>
/// Constrains a layer index to be inside the range between 0 and <see cref="LastLayerIndex"/>
/// </summary>
/// <param name="layerIndex">Layer index to sanitize</param>
@@ -5695,6 +5740,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
return originalValue != layerIndex;
}
+ public uint SanitizeLayerIndex(uint layerIndex)
+ {
+ return Math.Min(layerIndex, LastLayerIndex);
+ }
+
/// <summary>
/// Re-assign layer indexes and parent <see cref="FileFormat"/>
/// </summary>
diff --git a/UVtools.Core/FileFormats/GenericZIPFile.cs b/UVtools.Core/FileFormats/GenericZIPFile.cs
index 06dad20..44b4e50 100644
--- a/UVtools.Core/FileFormats/GenericZIPFile.cs
+++ b/UVtools.Core/FileFormats/GenericZIPFile.cs
@@ -23,7 +23,7 @@ using UVtools.Core.Operations;
namespace UVtools.Core.FileFormats;
#region Sub Classes
-[Serializable]
+
[XmlRoot(ElementName = "Manifest")]
public class GenericZipManifest
{
diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs
index d3559e7..497bc6b 100644
--- a/UVtools.Core/FileFormats/JXSFile.cs
+++ b/UVtools.Core/FileFormats/JXSFile.cs
@@ -481,7 +481,7 @@ public class JXSFile : FileFormat
if(property != keyValue[0]) continue;
//Debug.WriteLine(attribute.Name);
- Helpers.SetPropertyValue(propertyInfo, ConfigFile, keyValue[1]);
+ propertyInfo.SetValueFromString(ConfigFile, keyValue[1]);
}
}
diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs
index 21af6a3..ca2df92 100644
--- a/UVtools.Core/FileFormats/SL1File.cs
+++ b/UVtools.Core/FileFormats/SL1File.cs
@@ -655,7 +655,7 @@ public class SL1File : FileFormat
var attribute = obj.GetType().GetProperty(fieldName);
if (attribute is null || !attribute.CanWrite) continue;
//Debug.WriteLine(attribute.Name);
- Helpers.SetPropertyValue(attribute, obj, keyValue[1]);
+ attribute.SetValueFromString(obj, keyValue[1]);
Statistics.ImplementedKeys.Add(keyValue[0]);
foundMember = true;
diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs
index 9ef424d..76abdf3 100644
--- a/UVtools.Core/FileFormats/VDAFile.cs
+++ b/UVtools.Core/FileFormats/VDAFile.cs
@@ -20,14 +20,14 @@ using UVtools.Core.Operations;
namespace UVtools.Core.FileFormats;
#region Sub Classes
-[Serializable]
+
[XmlRoot(ElementName = "root")]
public class VDARoot
{
- [Serializable]
+
public class VDAFileInfo
{
- [Serializable]
+
public class VDAVersion
{
public ushort Major { get; set; } = 1;
@@ -35,10 +35,10 @@ public class VDARoot
}
- [Serializable]
+
public class VDAWritten
{
- [Serializable]
+
[XmlRoot(ElementName = "By")]
public class VDABy
{
@@ -77,7 +77,7 @@ public class VDARoot
public VDAWritten Written { get; set; } = new();
}
- [Serializable]
+
public class VDASlices
{
public ushort Count { get; set; } = 1;
@@ -95,7 +95,7 @@ public class VDARoot
public uint LayerCount { get; set; }
}
- [Serializable]
+
public class VDAMachines
{
public string FileType { get; set; } = "ZIP File";
diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs
index bdaaf1b..30ab1f4 100644
--- a/UVtools.Core/FileFormats/ZCodeFile.cs
+++ b/UVtools.Core/FileFormats/ZCodeFile.cs
@@ -27,11 +27,11 @@ using UVtools.Core.Operations;
namespace UVtools.Core.FileFormats;
-[Serializable]
+
[XmlRoot(ElementName = "Print")]
public class ZCodePrint
{
- [Serializable]
+
[XmlRoot(ElementName = "Device")]
public class ZcodePrintDevice
{
@@ -52,14 +52,14 @@ public class ZCodePrint
}
- [Serializable]
+
[XmlRoot(ElementName = "Profile")]
public class ZcodePrintProfile
{
[XmlAttribute("name")]
public string Name { get; set; } = "UVtools";
- [Serializable]
+
[XmlRoot(ElementName = "Slice")]
public class ZcodePrintProfileSlice
{
@@ -106,7 +106,7 @@ public class ZCodePrint
public ZcodePrintProfileSlice Slice { get; set; } = new();
}
- [Serializable]
+
[XmlRoot(ElementName = "Job")]
public class ZcodePrintJob
{
diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs
index 3ac95cb..fb6e8ac 100644
--- a/UVtools.Core/Helpers.cs
+++ b/UVtools.Core/Helpers.cs
@@ -44,63 +44,6 @@ public static class Helpers
return fs.WriteStream(stream, offset);
}
- public static bool SetPropertyValue(PropertyInfo attribute, object obj, string value)
- {
- var name = attribute.PropertyType.Name.ToLower();
- switch (name)
- {
- case "string":
- attribute.SetValue(obj, value.Convert<string>());
- return true;
- case "boolean":
- if(char.IsDigit(value[0])) attribute.SetValue(obj, !value.Equals("0"));
- else attribute.SetValue(obj, value.Equals("True", StringComparison.OrdinalIgnoreCase));
- return true;
- case "byte":
- if (value.Equals("true", StringComparison.OrdinalIgnoreCase)) attribute.SetValue(obj, (byte)1);
- else if (value.Equals("false", StringComparison.OrdinalIgnoreCase)) attribute.SetValue(obj, byte.MinValue);
- else attribute.SetValue(obj, value.Convert<byte>());
- return true;
- case "sbyte":
- attribute.SetValue(obj, value.Convert<sbyte>());
- return true;
- case "uint16":
- attribute.SetValue(obj, value.Convert<ushort>());
- return true;
- case "int16":
- attribute.SetValue(obj, value.Convert<short>());
- return true;
- case "uint32":
- attribute.SetValue(obj, value.Convert<uint>());
- return true;
- case "int32":
- attribute.SetValue(obj, value.Convert<int>());
- return true;
- case "uint64":
- attribute.SetValue(obj, value.Convert<ulong>());
- return true;
- case "int64":
- attribute.SetValue(obj, value.Convert<long>());
- return true;
- case "single":
- attribute.SetValue(obj, (float)Math.Round(float.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- case "double":
- attribute.SetValue(obj, Math.Round(double.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- case "decimal":
- attribute.SetValue(obj, Math.Round(decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- default:
- throw new Exception($"Data type '{name}' not recognized, contact developer.");
- }
- }
-
- public static int GetPixelPosition(int width, int x, int y)
- {
- return width * y + x;
- }
-
public static void SwapVariables<T>(ref T var1, ref T var2)
{
(var1, var2) = (var2, var1);
diff --git a/UVtools.Core/Layers/Issue.cs b/UVtools.Core/Layers/Issue.cs
index cff3109..a421b7c 100644
--- a/UVtools.Core/Layers/Issue.cs
+++ b/UVtools.Core/Layers/Issue.cs
@@ -9,6 +9,7 @@
using System;
using System.Drawing;
using System.Text.Json.Serialization;
+using System.Xml.Serialization;
using UVtools.Core.Extensions;
namespace UVtools.Core.Layers;
@@ -22,6 +23,7 @@ public class Issue
/// Gets the issue type associated
/// </summary>
[JsonIgnore]
+ [XmlIgnore]
public MainIssue? Parent { get; internal set; }
public MainIssue.IssueType? Type => Parent?.Type;
@@ -30,6 +32,7 @@ public class Issue
/// Gets the layer where this issue is present
/// </summary>
[JsonIgnore]
+ [XmlIgnore]
public Layer Layer { get; init; }
/// <summary>
@@ -54,6 +57,8 @@ public class Issue
public Point FirstPoint { get; init; } = new(-1,-1);
+ public Issue() { }
+
public Issue(Layer layer, Rectangle boundingRectangle, double area)
{
Layer = layer;
diff --git a/UVtools.Core/Layers/IssueOfContours.cs b/UVtools.Core/Layers/IssueOfContours.cs
index 9b83464..202973f 100644
--- a/UVtools.Core/Layers/IssueOfContours.cs
+++ b/UVtools.Core/Layers/IssueOfContours.cs
@@ -20,6 +20,8 @@ public sealed class IssueOfContours : Issue
/// </summary>
public Point[][] Contours { get; init; }
+ public IssueOfContours() { }
+
public IssueOfContours(Layer layer, IEnumerable<Point[]> contours, Rectangle boundingRectangle, double area) : base(layer, boundingRectangle, area)
{
Contours = contours.ToArray();
diff --git a/UVtools.Core/Layers/IssueOfPoints.cs b/UVtools.Core/Layers/IssueOfPoints.cs
index fd335bc..d8b2a5a 100644
--- a/UVtools.Core/Layers/IssueOfPoints.cs
+++ b/UVtools.Core/Layers/IssueOfPoints.cs
@@ -19,6 +19,8 @@ public sealed class IssueOfPoints : Issue
/// </summary>
public Point[] Points { get; init; }
+ public IssueOfPoints() { }
+
public IssueOfPoints(Layer layer, IEnumerable<Point> points, Rectangle boundingRectangle = default) : base(layer, boundingRectangle, points.Count())
{
Points = points.ToArray();
diff --git a/UVtools.Core/Layers/LayerIssueConfiguration.cs b/UVtools.Core/Layers/LayerIssueConfiguration.cs
index 27caebd..70055b8 100644
--- a/UVtools.Core/Layers/LayerIssueConfiguration.cs
+++ b/UVtools.Core/Layers/LayerIssueConfiguration.cs
@@ -6,6 +6,7 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Collections.Generic;
namespace UVtools.Core.Layers;
@@ -14,19 +15,32 @@ namespace UVtools.Core.Layers;
public sealed class IssuesDetectionConfiguration
{
- public IslandDetectionConfiguration IslandConfig { get; }
- public OverhangDetectionConfiguration OverhangConfig { get; }
- public ResinTrapDetectionConfiguration ResinTrapConfig { get; }
- public TouchingBoundDetectionConfiguration TouchingBoundConfig { get; }
- public PrintHeightDetectionConfiguration PrintHeightConfig { get; }
- public bool EmptyLayerConfig { get; }
-
- public IssuesDetectionConfiguration(IslandDetectionConfiguration islandConfig,
+ public IslandDetectionConfiguration IslandConfig { get; set; } = new();
+ public OverhangDetectionConfiguration OverhangConfig { get; set; } = new();
+ public ResinTrapDetectionConfiguration ResinTrapConfig { get; set; } = new();
+ public TouchingBoundDetectionConfiguration TouchingBoundConfig { get; set; } = new();
+ public PrintHeightDetectionConfiguration PrintHeightConfig { get; set; } = new();
+ public EmptyLayerDetectionConfiguration EmptyLayerConfig { get; set; } = new();
+
+ public DetectionConfiguration[] Configurations => new DetectionConfiguration[]
+ {
+ IslandConfig,
+ OverhangConfig,
+ ResinTrapConfig,
+ TouchingBoundConfig,
+ PrintHeightConfig,
+ EmptyLayerConfig
+ };
+
+ public IssuesDetectionConfiguration() { }
+
+ public IssuesDetectionConfiguration(
+ IslandDetectionConfiguration islandConfig,
OverhangDetectionConfiguration overhangConfig,
ResinTrapDetectionConfiguration resinTrapConfig,
TouchingBoundDetectionConfiguration touchingBoundConfig,
- PrintHeightDetectionConfiguration printHeightConfig,
- bool emptyLayerConfig)
+ PrintHeightDetectionConfiguration printHeightConfig,
+ EmptyLayerDetectionConfiguration emptyLayerConfig)
{
IslandConfig = islandConfig;
OverhangConfig = overhangConfig;
@@ -35,15 +49,73 @@ public sealed class IssuesDetectionConfiguration
PrintHeightConfig = printHeightConfig;
EmptyLayerConfig = emptyLayerConfig;
}
+
+ public void Deconstruct(out IslandDetectionConfiguration islandConfig, out OverhangDetectionConfiguration overhangConfig, out ResinTrapDetectionConfiguration resinTrapConfig, out TouchingBoundDetectionConfiguration touchingBoundConfig, out PrintHeightDetectionConfiguration printHeightConfig, out EmptyLayerDetectionConfiguration emptyLayerConfig)
+ {
+ islandConfig = IslandConfig;
+ overhangConfig = OverhangConfig;
+ resinTrapConfig = ResinTrapConfig;
+ touchingBoundConfig = TouchingBoundConfig;
+ printHeightConfig = PrintHeightConfig;
+ emptyLayerConfig = EmptyLayerConfig;
+ }
+
+
+ public void EnableAll()
+ {
+ foreach (var config in Configurations)
+ {
+ config.Enabled = true;
+ }
+ }
+
+ public void DisableAll()
+ {
+ foreach (var config in Configurations)
+ {
+ config.Enabled = false;
+ }
+ }
+
+ public IssuesDetectionConfiguration Clone()
+ {
+ var config = new IssuesDetectionConfiguration(
+ (IslandDetectionConfiguration) IslandConfig.Clone(),
+ (OverhangDetectionConfiguration) OverhangConfig.Clone(),
+ (ResinTrapDetectionConfiguration) ResinTrapConfig.Clone(),
+ (TouchingBoundDetectionConfiguration) TouchingBoundConfig.Clone(),
+ (PrintHeightDetectionConfiguration) PrintHeightConfig.Clone(),
+ (EmptyLayerDetectionConfiguration) EmptyLayerConfig.Clone()
+ );
+ return config;
+ }
}
-public sealed class IslandDetectionConfiguration
+public class DetectionConfiguration : ICloneable
{
/// <summary>
/// Gets or sets if the detection is enabled
/// </summary>
public bool Enabled { get; set; } = true;
+ public DetectionConfiguration() { }
+
+ public DetectionConfiguration(bool enabled)
+ {
+ Enabled = enabled;
+ }
+
+ public void Enable() => Enabled = true;
+ public void Disable() => Enabled = false;
+
+ public object Clone()
+ {
+ return MemberwiseClone();
+ }
+}
+
+public sealed class IslandDetectionConfiguration : DetectionConfiguration
+{
/// <summary>
/// Gets or sets a list of layers to check for islands, absent layers will not be checked.
/// Set to null to check every layer
@@ -96,28 +168,19 @@ public sealed class IslandDetectionConfiguration
/// </summary>
public byte RequiredPixelBrightnessToSupport { get; set; } = 150;
- public IslandDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ public IslandDetectionConfiguration()
+ { }
- public IslandDetectionConfiguration Clone()
- {
- return (MemberwiseClone() as IslandDetectionConfiguration)!;
- }
+ public IslandDetectionConfiguration(bool enabled) : base(enabled)
+ { }
}
/// <summary>
/// Overhang configuration
/// </summary>
-public sealed class OverhangDetectionConfiguration
+public sealed class OverhangDetectionConfiguration : DetectionConfiguration
{
/// <summary>
- /// Gets or sets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
/// Gets or sets a list of layers to check for overhangs, absent layers will not be checked.
/// Set to null to check every layer
/// </summary>
@@ -139,20 +202,16 @@ public sealed class OverhangDetectionConfiguration
/// </summary>
public byte ErodeIterations { get; set; } = 40;
- public OverhangDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ public OverhangDetectionConfiguration()
+ { }
+
+ public OverhangDetectionConfiguration(bool enabled) : base(enabled)
+ { }
}
-public sealed class ResinTrapDetectionConfiguration
+public sealed class ResinTrapDetectionConfiguration : DetectionConfiguration
{
/// <summary>
- /// Gets or sets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
/// Gets or sets the starting layer index for the detection which will also be considered a drain layer.
/// Use this setting to bypass complicated rafts by selected the model first real layer.
/// </summary>
@@ -167,7 +226,7 @@ public sealed class ResinTrapDetectionConfiguration
/// <summary>
/// Gets the required area size (x*y) to consider process a hollow area (0-255)
/// </summary>
- public byte RequiredAreaToProcessCheck { get; set; } = 1;
+ public byte RequiredAreaToProcessCheck { get; set; } = 4;
/// <summary>
/// Gets the number of black pixels required to consider a drain
@@ -195,21 +254,17 @@ public sealed class ResinTrapDetectionConfiguration
public decimal RequiredHeightToConsiderSuctionCup { get; set; } = 0.5m;
- public ResinTrapDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ public ResinTrapDetectionConfiguration()
+ { }
+
+ public ResinTrapDetectionConfiguration(bool enabled) : base(enabled)
+ { }
}
-public sealed class TouchingBoundDetectionConfiguration
+public sealed class TouchingBoundDetectionConfiguration : DetectionConfiguration
{
/// <summary>
- /// Gets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
/// Gets the minimum pixel brightness to be a touching bound
/// </summary>
public byte MinimumPixelBrightness { get; set; } = 127;
@@ -235,28 +290,52 @@ public sealed class TouchingBoundDetectionConfiguration
public byte MarginBottom { get; set; } = 5;
- public TouchingBoundDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ public TouchingBoundDetectionConfiguration()
+ { }
+
+ public TouchingBoundDetectionConfiguration(bool enabled) : base(enabled)
+ { }
}
-public sealed class PrintHeightDetectionConfiguration
+public sealed class PrintHeightDetectionConfiguration : DetectionConfiguration
{
/// <summary>
- /// Gets if the detection is enabled
+ /// Get the offset from top to sum to printer max Z height
/// </summary>
- public bool Enabled { get; set; } = true;
+ public float Offset { get; set; }
+
+ public PrintHeightDetectionConfiguration()
+ { }
+
+ public PrintHeightDetectionConfiguration(bool enabled) : base(enabled)
+ { }
+}
+public sealed class EmptyLayerDetectionConfiguration : DetectionConfiguration
+{
/// <summary>
- /// Get the offset from top to sum to printer max Z height
+ /// <para>Gets or sets to ignore the starting empty layers.</para>
+ /// <para>True to ignore starting empty layers, otherwise false.</para>
/// </summary>
- public float Offset { get; set; }
+ public bool IgnoreStartingEmptyLayers { get; set; }
- public PrintHeightDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ /// <summary>
+ /// <para>Gets or sets to ignore the loose empty layers that are not on start nor in end.</para>
+ /// <para>True to ignore loose empty layers, otherwise false.</para>
+ /// </summary>
+ public bool IgnoreLooseEmptyLayers { get; set; }
+
+ /// <summary>
+ /// <para>Gets or sets to ignore the ending empty layers.</para>
+ /// <para>True to ignore ending empty layers, otherwise false.</para>
+ /// </summary>
+ public bool IgnoreEndingEmptyLayers { get; set; }
+
+ public EmptyLayerDetectionConfiguration()
+ { }
+
+ public EmptyLayerDetectionConfiguration(bool enabled) : base(enabled)
+ { }
}
#endregion \ No newline at end of file
diff --git a/UVtools.Core/Layers/MainIssue.cs b/UVtools.Core/Layers/MainIssue.cs
index bc25d22..a6efa58 100644
--- a/UVtools.Core/Layers/MainIssue.cs
+++ b/UVtools.Core/Layers/MainIssue.cs
@@ -12,10 +12,13 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text.Json.Serialization;
+using System.Xml.Serialization;
using UVtools.Core.Extensions;
namespace UVtools.Core.Layers;
+[XmlInclude(typeof(IssueOfContours))]
+[XmlInclude(typeof(IssueOfPoints))]
public class MainIssue : IReadOnlyList<Issue>
{
public enum IssueType : byte
@@ -97,9 +100,16 @@ public class MainIssue : IReadOnlyList<Issue>
public double Area { get; init; }
/// <summary>
+ /// Gets the area character, either ² or ³
+ /// </summary>
+ public char AreaChar => LayerRangeCount > 1 ? '³' : '²';
+
+ /// <summary>
/// Gets all issues inside this main issue
/// </summary>
- public Issue[] Childs { get; init; } = null!;
+ public Issue[] Childs { get; init; } = Array.Empty<Issue>();
+
+ public MainIssue() { }
public MainIssue(IssueType type, Rectangle boundingRectangle = default)
{
@@ -118,31 +128,38 @@ public class MainIssue : IReadOnlyList<Issue>
public MainIssue(IssueType type, IEnumerable<Issue> issues) : this(type)
{
- var boundingRectangle = Rectangle.Empty;
- double area = 0;
foreach (var issue in issues)
{
issue.Parent = this;
- area += issue.Area / issue.Layer.LayerHeight;
+ var layerHeightInPixels = issue.Layer.SlicerFile.MillimetersToPixels(issue.Layer.LayerHeight, 20);
+ Area += issue.Area * layerHeightInPixels;
PixelCount += issue.PixelsCount;
if (issue.BoundingRectangle.IsEmpty) continue;
- if (boundingRectangle.IsEmpty)
+ if (BoundingRectangle.IsEmpty)
{
- boundingRectangle = issue.BoundingRectangle;
+ BoundingRectangle = issue.BoundingRectangle;
continue;
}
- boundingRectangle.Intersect(issue.BoundingRectangle);
+ BoundingRectangle = Rectangle.Union(BoundingRectangle, issue.BoundingRectangle);
}
- BoundingRectangle = boundingRectangle;
- Area = area;
- Childs = issues.OrderBy(issue => issue.LayerIndex).ToArray();
- Sort();
+ if (Childs.Length == 1)
+ {
+ Area = Childs[0].Area;
+ Childs = issues.ToArray();
+ }
+ else
+ {
+ Childs = issues.OrderBy(issue => issue.LayerIndex).ToArray();
+ }
+
+ Area = Math.Floor(Area);
}
private void Sort()
{
+ if (Childs.Length < 1) return;
Array.Sort(Childs, (issue, issue1) => issue.LayerIndex.CompareTo(issue1.LayerIndex));
}
diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs
index 80d02d0..68467a2 100644
--- a/UVtools.Core/Managers/IssueManager.cs
+++ b/UVtools.Core/Managers/IssueManager.cs
@@ -117,22 +117,20 @@ public sealed class IssueManager : RangeObservableCollection<MainIssue>
return GetIssuesBy(this, layerIndex);
}
- public List<MainIssue> DetectIssues(
- IslandDetectionConfiguration? islandConfig = null,
- OverhangDetectionConfiguration? overhangConfig = null,
- ResinTrapDetectionConfiguration? resinTrapConfig = null,
- TouchingBoundDetectionConfiguration? touchBoundConfig = null,
- PrintHeightDetectionConfiguration? printHeightConfig = null,
- bool emptyLayersConfig = true,
- OperationProgress? progress = null)
+ public List<MainIssue> DetectIssues(IssuesDetectionConfiguration? config = null, OperationProgress? progress = null)
{
if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return new List<MainIssue>();
- islandConfig ??= new IslandDetectionConfiguration();
- overhangConfig ??= new OverhangDetectionConfiguration();
- resinTrapConfig ??= new ResinTrapDetectionConfiguration();
- touchBoundConfig ??= new TouchingBoundDetectionConfiguration();
- printHeightConfig ??= new PrintHeightDetectionConfiguration();
+ config ??= new IssuesDetectionConfiguration();
+ var (
+ islandConfig,
+ overhangConfig,
+ resinTrapConfig,
+ touchBoundConfig,
+ printHeightConfig,
+ emptyLayerConfig
+ ) = config;
+
progress ??= new OperationProgress();
var result = new ConcurrentBag<MainIssue>();
@@ -175,11 +173,52 @@ public sealed class IssueManager : RangeObservableCollection<MainIssue>
}
}
- if (emptyLayersConfig)
+ if (emptyLayerConfig.Enabled)
{
- foreach (var layer in SlicerFile)
+ for (var layerIndex = 0; layerIndex < SlicerFile.Count; layerIndex++)
{
- if (layer.IsEmpty)
+ var layer = SlicerFile[layerIndex];
+ if (!layer.IsEmpty) continue;
+
+ if (!emptyLayerConfig.IgnoreStartingEmptyLayers
+ && !emptyLayerConfig.IgnoreLooseEmptyLayers
+ && !emptyLayerConfig.IgnoreEndingEmptyLayers)
+ {
+ AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
+ continue;
+ }
+
+ // 1 = Starting
+ // 2 = Loose
+ // 3 = Ending
+ byte emptyLayerPosType = 0;
+ int i;
+
+ for (i = 0; i < layerIndex && SlicerFile[i].IsEmpty; layerIndex++) { }
+
+ if (i == layerIndex)
+ {
+ emptyLayerPosType = 1;
+ }
+ else
+ {
+ for (i = (int) SlicerFile.LastLayerIndex; i > layerIndex && SlicerFile[i].IsEmpty; layerIndex--) { }
+ emptyLayerPosType = i == layerIndex ? (byte) 3 : (byte) 2;
+ }
+
+ if (emptyLayerPosType == 1)
+ {
+ if(!emptyLayerConfig.IgnoreStartingEmptyLayers) AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
+ }
+ else if (emptyLayerPosType == 2)
+ {
+ if (!emptyLayerConfig.IgnoreLooseEmptyLayers) AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
+ }
+ else if (emptyLayerPosType == 3)
+ {
+ if (!emptyLayerConfig.IgnoreEndingEmptyLayers) AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
+ }
+ else
{
AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
}
diff --git a/UVtools.Core/Managers/SuggestionManager.cs b/UVtools.Core/Managers/SuggestionManager.cs
index cd6b272..41c38cb 100644
--- a/UVtools.Core/Managers/SuggestionManager.cs
+++ b/UVtools.Core/Managers/SuggestionManager.cs
@@ -16,7 +16,7 @@ using UVtools.Core.Suggestions;
namespace UVtools.WPF.Structures;
-[Serializable]
+
public class SuggestionManager
{
#region Properties
diff --git a/UVtools.Core/Objects/ExposureItem.cs b/UVtools.Core/Objects/ExposureItem.cs
index 90a16ad..5766c86 100644
--- a/UVtools.Core/Objects/ExposureItem.cs
+++ b/UVtools.Core/Objects/ExposureItem.cs
@@ -10,7 +10,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Objects;
-[Serializable]
+
public sealed class ExposureItem : BindableBase, IComparable<ExposureItem>
{
private decimal _layerHeight;
diff --git a/UVtools.Core/Objects/GenericFileRepresentation.cs b/UVtools.Core/Objects/GenericFileRepresentation.cs
index f4b7c2d..2d3825e 100644
--- a/UVtools.Core/Objects/GenericFileRepresentation.cs
+++ b/UVtools.Core/Objects/GenericFileRepresentation.cs
@@ -10,7 +10,7 @@ using System.IO;
namespace UVtools.Core.Objects;
-[Serializable]
+
public class GenericFileRepresentation : BindableBase, ICloneable,
IComparable<GenericFileRepresentation>, IEquatable<GenericFileRepresentation>,
IComparable<string>, IEquatable<string>
diff --git a/UVtools.Core/Objects/KernelConfiguration.cs b/UVtools.Core/Objects/KernelConfiguration.cs
index 9c967bb..6c17089 100644
--- a/UVtools.Core/Objects/KernelConfiguration.cs
+++ b/UVtools.Core/Objects/KernelConfiguration.cs
@@ -18,7 +18,7 @@ using UVtools.Core.Managers;
namespace UVtools.Core.Objects;
-[Serializable]
+
public sealed class KernelConfiguration : BindableBase, IDisposable
{
#region Members
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index 1144497..d545cf4 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -21,7 +21,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public abstract class Operation : BindableBase, IDisposable
{
#region Constants
diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs
index b9fc536..39d8c7d 100644
--- a/UVtools.Core/Operations/OperationBlur.cs
+++ b/UVtools.Core/Operations/OperationBlur.cs
@@ -18,7 +18,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationBlur : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs
index 1be34b5..15bf27b 100644
--- a/UVtools.Core/Operations/OperationCalculator.cs
+++ b/UVtools.Core/Operations/OperationCalculator.cs
@@ -14,7 +14,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationCalculator : Operation
{
#region Overrides
diff --git a/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs b/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs
index 12d6945..a439480 100644
--- a/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs
+++ b/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs
@@ -19,7 +19,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateBloomingEffect : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
index 9ffc5e1..ee0cddb 100644
--- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
+++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
@@ -22,7 +22,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateElephantFoot : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 45ba400..4e3906c 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -26,7 +26,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateExposureFinder : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
index acf6e71..c9b1804 100644
--- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
+++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
@@ -20,7 +20,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateGrayscale : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
index 45d81ac..0fd570b 100644
--- a/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
+++ b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
@@ -18,7 +18,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateLiftHeight : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
index ab14dec..f9e1da9 100644
--- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs
+++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
@@ -19,7 +19,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateStressTower : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
index 7179fa9..6efd152 100644
--- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs
+++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
@@ -20,7 +20,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateTolerance : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
index 6de4c4f..0a45422 100644
--- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
+++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
@@ -19,7 +19,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationCalibrateXYZAccuracy : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs
index b9bd0aa..409032e 100644
--- a/UVtools.Core/Operations/OperationChangeResolution.cs
+++ b/UVtools.Core/Operations/OperationChangeResolution.cs
@@ -16,7 +16,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationChangeResolution : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationDoubleExposure.cs b/UVtools.Core/Operations/OperationDoubleExposure.cs
index 0123d68..ac75e16 100644
--- a/UVtools.Core/Operations/OperationDoubleExposure.cs
+++ b/UVtools.Core/Operations/OperationDoubleExposure.cs
@@ -19,7 +19,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationDoubleExposure : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
index 2d7e0ff..cee2d0f 100644
--- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
+++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
@@ -23,7 +23,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationDynamicLayerHeight : Operation
{
#region Sub Classes
diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs
index 78c199c..11a08d0 100644
--- a/UVtools.Core/Operations/OperationDynamicLifts.cs
+++ b/UVtools.Core/Operations/OperationDynamicLifts.cs
@@ -16,7 +16,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationDynamicLifts : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs
index 35726fd..35ee122 100644
--- a/UVtools.Core/Operations/OperationEditParameters.cs
+++ b/UVtools.Core/Operations/OperationEditParameters.cs
@@ -14,7 +14,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationEditParameters : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationFadeExposureTime.cs b/UVtools.Core/Operations/OperationFadeExposureTime.cs
index e0715e3..cc92577 100644
--- a/UVtools.Core/Operations/OperationFadeExposureTime.cs
+++ b/UVtools.Core/Operations/OperationFadeExposureTime.cs
@@ -13,7 +13,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationFadeExposureTime : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs
index cc29c04..d54b279 100644
--- a/UVtools.Core/Operations/OperationFlip.cs
+++ b/UVtools.Core/Operations/OperationFlip.cs
@@ -14,7 +14,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationFlip : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationIPrintedThisFile.cs b/UVtools.Core/Operations/OperationIPrintedThisFile.cs
index f29cb1c..fc2a12f 100644
--- a/UVtools.Core/Operations/OperationIPrintedThisFile.cs
+++ b/UVtools.Core/Operations/OperationIPrintedThisFile.cs
@@ -14,7 +14,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationIPrintedThisFile : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs
index 9d0c4df..74c4835 100644
--- a/UVtools.Core/Operations/OperationInfill.cs
+++ b/UVtools.Core/Operations/OperationInfill.cs
@@ -20,7 +20,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationInfill : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs
index 7e027a7..748194b 100644
--- a/UVtools.Core/Operations/OperationLayerArithmetic.cs
+++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs
@@ -17,7 +17,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationLayerArithmetic : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs
index 9c591b9..2480656 100644
--- a/UVtools.Core/Operations/OperationLayerClone.cs
+++ b/UVtools.Core/Operations/OperationLayerClone.cs
@@ -14,7 +14,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerClone : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerExportGif.cs b/UVtools.Core/Operations/OperationLayerExportGif.cs
index cd4a0ed..e2fb4c5 100644
--- a/UVtools.Core/Operations/OperationLayerExportGif.cs
+++ b/UVtools.Core/Operations/OperationLayerExportGif.cs
@@ -21,7 +21,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportGif : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
index 56c0a2d..3f176de 100644
--- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
+++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
@@ -17,7 +17,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportHeatMap : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerExportHtml.cs b/UVtools.Core/Operations/OperationLayerExportHtml.cs
index 00465e8..17d4f73 100644
--- a/UVtools.Core/Operations/OperationLayerExportHtml.cs
+++ b/UVtools.Core/Operations/OperationLayerExportHtml.cs
@@ -20,7 +20,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportHtml : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs
index 0579ac6..18d805e 100644
--- a/UVtools.Core/Operations/OperationLayerExportImage.cs
+++ b/UVtools.Core/Operations/OperationLayerExportImage.cs
@@ -18,7 +18,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportImage : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationLayerExportMesh.cs b/UVtools.Core/Operations/OperationLayerExportMesh.cs
index ef03c3c..b3382f4 100644
--- a/UVtools.Core/Operations/OperationLayerExportMesh.cs
+++ b/UVtools.Core/Operations/OperationLayerExportMesh.cs
@@ -25,7 +25,7 @@ using UVtools.Core.Voxel;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportMesh : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
index 9212a36..efc677f 100644
--- a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
+++ b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
@@ -14,7 +14,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerExportSkeleton : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index d7a75ba..439f34d 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -22,7 +22,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerImport : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs
index 682ddaa..cb65550 100644
--- a/UVtools.Core/Operations/OperationLayerReHeight.cs
+++ b/UVtools.Core/Operations/OperationLayerReHeight.cs
@@ -20,7 +20,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerReHeight : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs
index 20e53ba..1fa4145 100644
--- a/UVtools.Core/Operations/OperationLayerRemove.cs
+++ b/UVtools.Core/Operations/OperationLayerRemove.cs
@@ -15,7 +15,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationLayerRemove : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationLightBleedCompensation.cs b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
index d307a71..c4c153d 100644
--- a/UVtools.Core/Operations/OperationLightBleedCompensation.cs
+++ b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
@@ -19,7 +19,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationLightBleedCompensation : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationLithophane.cs b/UVtools.Core/Operations/OperationLithophane.cs
index 9b076d0..f282ce1 100644
--- a/UVtools.Core/Operations/OperationLithophane.cs
+++ b/UVtools.Core/Operations/OperationLithophane.cs
@@ -22,7 +22,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationLithophane : Operation
{
#region Enum
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index 348f3de..29a0d82 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -18,7 +18,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationMask : Operation
{
#region Overrides
diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs
index 284cba1..5462b2e 100644
--- a/UVtools.Core/Operations/OperationMorph.cs
+++ b/UVtools.Core/Operations/OperationMorph.cs
@@ -16,7 +16,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationMorph : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs
index fd0062e..4948705 100644
--- a/UVtools.Core/Operations/OperationMove.cs
+++ b/UVtools.Core/Operations/OperationMove.cs
@@ -15,7 +15,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationMove : Operation
{
#region Overrides
diff --git a/UVtools.Core/Operations/OperationPCBExposure.cs b/UVtools.Core/Operations/OperationPCBExposure.cs
index 7b0fea9..0529085 100644
--- a/UVtools.Core/Operations/OperationPCBExposure.cs
+++ b/UVtools.Core/Operations/OperationPCBExposure.cs
@@ -23,7 +23,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationPCBExposure : Operation
{
#region Sub Classes
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index 6666582..8b98001 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -16,7 +16,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationPattern : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs
index 8690208..6679bb7 100644
--- a/UVtools.Core/Operations/OperationPixelArithmetic.cs
+++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs
@@ -22,7 +22,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationPixelArithmetic : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index 87465f8..b0346fb 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -19,7 +19,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationPixelDimming : Operation
{
#region Subclasses
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index 5e9cb2f..c6f0ea8 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -21,7 +21,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationRaftRelief : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
index 65c9e69..3dc4a7b 100644
--- a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
+++ b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
@@ -14,7 +14,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationRaiseOnPrintFinish : Operation
{
#region Constants
diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs
index 479d51d..a769a95 100644
--- a/UVtools.Core/Operations/OperationRedrawModel.cs
+++ b/UVtools.Core/Operations/OperationRedrawModel.cs
@@ -18,7 +18,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationRedrawModel : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index a3ac3ea..e0806b8 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -12,13 +12,10 @@ using Emgu.CV.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using System.Xml.Serialization;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
@@ -26,10 +23,11 @@ using UVtools.Core.Managers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationRepairLayers : Operation
{
#region Members
+ private bool _detectIssues;
private bool _repairIslands = true;
private bool _repairResinTraps = true;
private bool _repairSuctionCups;
@@ -50,10 +48,10 @@ public class OperationRepairLayers : Operation
public override string Title => "Repair layers and issues";
public override string Description => string.Empty;
- public override string ConfirmationText => "attempt this repair?";
+ public override string ConfirmationText => "attempt this repair?";
public override string ProgressTitle =>
- $"Reparing layers {LayerIndexStart} through {LayerIndexEnd}";
+ $"Repairing layers {LayerIndexStart} through {LayerIndexEnd}";
public override string ProgressAction => "Repaired layers";
@@ -66,6 +64,12 @@ public class OperationRepairLayers : Operation
sb.AppendLine("You must select at least one repair operation.");
}
+ if (!_detectIssues && SlicerFile.IssueManager.Count == 0)
+ {
+ sb.AppendLine("There are no present issues on the current session to repair.");
+ sb.AppendLine("Please detect issues before run this tool or check the option: \"Re-detect the selected issues before repair\" to force a detect and repair.");
+ }
+
return sb.ToString();
}
@@ -76,7 +80,7 @@ public class OperationRepairLayers : Operation
if(_repairResinTraps) repair.Add("Resin traps");
if(_repairSuctionCups) repair.Add("Suction cups");
if(_removeEmptyLayers) repair.Add("Empty layers");
- var result = $"[Repair: {string.Join('/', repair)}] " +
+ var result = $"[Repair: {string.Join('/', repair)}] [Detect: {_detectIssues}]" +
$"[Gap closing: {_gapClosingIterations}px] " +
$"[Noise removal: {_noiseRemovalIterations}px]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
@@ -93,6 +97,16 @@ public class OperationRepairLayers : Operation
#endregion
#region Properties
+
+ /// <summary>
+ /// IF true it will re-detect the selected issues before repair, otherwise uses and repair the previous detected issues
+ /// </summary>
+ public bool DetectIssues
+ {
+ get => _detectIssues;
+ set => RaiseAndSetIfChanged(ref _detectIssues, value);
+ }
+
public bool RepairIslands
{
get => _repairIslands;
@@ -159,7 +173,7 @@ public class OperationRepairLayers : Operation
set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value);
}
- [XmlIgnore] public IslandDetectionConfiguration IslandDetectionConfig { get; set; } = new();
+ public IssuesDetectionConfiguration IssuesDetectionConfig { get; set; } = new();
#endregion
@@ -167,7 +181,7 @@ public class OperationRepairLayers : Operation
protected bool Equals(OperationRepairLayers other)
{
- return _repairIslands == other._repairIslands && _repairResinTraps == other._repairResinTraps && _removeEmptyLayers == other._removeEmptyLayers && _repairSuctionCups == other._repairSuctionCups && _removeIslandsBelowEqualPixelCount == other._removeIslandsBelowEqualPixelCount && _removeIslandsRecursiveIterations == other._removeIslandsRecursiveIterations && _attachIslandsBelowLayers == other._attachIslandsBelowLayers && _resinTrapsOverlapBy == other._resinTrapsOverlapBy && _suctionCupsVentHole == other._suctionCupsVentHole && _gapClosingIterations == other._gapClosingIterations && _noiseRemovalIterations == other._noiseRemovalIterations;
+ return _detectIssues == other._detectIssues && _repairIslands == other._repairIslands && _repairResinTraps == other._repairResinTraps && _repairSuctionCups == other._repairSuctionCups && _removeEmptyLayers == other._removeEmptyLayers && _removeIslandsBelowEqualPixelCount == other._removeIslandsBelowEqualPixelCount && _removeIslandsRecursiveIterations == other._removeIslandsRecursiveIterations && _attachIslandsBelowLayers == other._attachIslandsBelowLayers && _resinTrapsOverlapBy == other._resinTrapsOverlapBy && _suctionCupsVentHole == other._suctionCupsVentHole && _gapClosingIterations == other._gapClosingIterations && _noiseRemovalIterations == other._noiseRemovalIterations;
}
public override bool Equals(object? obj)
@@ -175,16 +189,17 @@ public class OperationRepairLayers : Operation
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationRepairLayers)obj);
+ return Equals((OperationRepairLayers) obj);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
+ hashCode.Add(_detectIssues);
hashCode.Add(_repairIslands);
hashCode.Add(_repairResinTraps);
- hashCode.Add(_removeEmptyLayers);
hashCode.Add(_repairSuctionCups);
+ hashCode.Add(_removeEmptyLayers);
hashCode.Add(_removeIslandsBelowEqualPixelCount);
hashCode.Add(_removeIslandsRecursiveIterations);
hashCode.Add(_attachIslandsBelowLayers);
@@ -201,7 +216,29 @@ public class OperationRepairLayers : Operation
protected override bool ExecuteInternally(OperationProgress progress)
{
- var issues = SlicerFile.IssueManager.GetVisible().ToList();
+ List<MainIssue> issues;
+
+ if (_detectIssues)
+ {
+ var config = IssuesDetectionConfig.Clone();
+ config.DisableAll();
+ config.IslandConfig.Enabled =
+ (_repairIslands && _removeIslandsBelowEqualPixelCount > 0 && _removeIslandsRecursiveIterations != 1) ||
+ _repairIslands && _attachIslandsBelowLayers > 0;
+ config.ResinTrapConfig.Enabled = _repairResinTraps;
+ config.ResinTrapConfig.DetectSuctionCups = _repairSuctionCups;
+ config.EmptyLayerConfig.Enabled = _removeEmptyLayers;
+
+ issues = SlicerFile.IssueManager.DetectIssues(config, progress).ToList();
+ issues.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue));
+ }
+ else
+ {
+ issues = SlicerFile.IssueManager.GetVisible().ToList();
+ }
+
+ if (issues.Count == 0) return true;
+
// Remove islands
if (//Issues is not null
//IslandDetectionConfig is not null
@@ -216,14 +253,9 @@ public class OperationRepairLayers : Operation
var recursiveIssues = issues;
var islandsToRecompute = new ConcurrentBag<uint>();
- var islandConfig = IslandDetectionConfig.Clone();
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
- var emptyLayersConfig = false;
-
- islandConfig.Enabled = true;
+ var config = IssuesDetectionConfig.Clone();
+ config.DisableAll();
+ config.IslandConfig.Enable();
//islandConfig.RequiredAreaToProcessCheck = (ushort)(_removeIslandsBelowEqualPixelCount / 2);
for (uint i = 0; i < limit; i++)
@@ -233,8 +265,8 @@ public class OperationRepairLayers : Operation
/*var whiteList = islandsToRecompute.GroupBy(u => u)
.Select(grp => grp.First())
.ToList();*/
- islandConfig.WhiteListLayers = islandsToRecompute.ToList();
- recursiveIssues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig);
+ config.IslandConfig.WhiteListLayers = islandsToRecompute.ToList();
+ recursiveIssues = SlicerFile.IssueManager.DetectIssues(config);
//Debug.WriteLine(i);
}
@@ -279,20 +311,15 @@ public class OperationRepairLayers : Operation
{
var islandsToProcess = issues;
- if (islandsToProcess.Count == 0)
+ /*if (islandsToProcess.Count == 0)
{
- var islandConfig = IslandDetectionConfig.Clone();
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
- var emptyLayersConfig = false;
+ var config = IssuesDetectionConfig.Clone();
+ config.DisableAll();
+ config.IslandConfig.Enable();
- islandConfig.Enabled = true;
-
- islandsToProcess = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig, progress);
+ islandsToProcess = SlicerFile.IssueManager.DetectIssues(config, progress);
islandsToProcess.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue));
- }
+ }*/
var issuesGroup = IssueManager.GetIssuesBy(islandsToProcess, MainIssue.IssueType.Island).GroupBy(issue => issue.LayerIndex);
@@ -318,7 +345,7 @@ public class OperationRepairLayers : Operation
foreach (IssueOfPoints issue in group)
{
int foundAt = startLayer == 0 ? 0 : - 1;
- var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IslandDetectionConfig.RequiredPixelsToSupportMultiplier);
+ var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IssuesDetectionConfig.IslandConfig.RequiredPixelsToSupportMultiplier);
for (var layerIndex = startLayer; layerIndex >= lowestPossibleLayer && foundAt < 0; layerIndex--)
{
@@ -330,7 +357,7 @@ public class OperationRepairLayers : Operation
foreach (var point in issue.Points)
{
- if (span[mat.GetPixelPos(point)] < IslandDetectionConfig.RequiredPixelBrightnessToSupport)
+ if (span[mat.GetPixelPos(point)] < IssuesDetectionConfig.IslandConfig.RequiredPixelBrightnessToSupport)
continue;
pixelsSupportingIsland++;
diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs
index 0f2fe56..4b57c59 100644
--- a/UVtools.Core/Operations/OperationResize.cs
+++ b/UVtools.Core/Operations/OperationResize.cs
@@ -15,7 +15,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationResize : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs
index cc747a8..970652b 100644
--- a/UVtools.Core/Operations/OperationRotate.cs
+++ b/UVtools.Core/Operations/OperationRotate.cs
@@ -14,7 +14,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationRotate : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationScripting.cs b/UVtools.Core/Operations/OperationScripting.cs
index 3161123..a0a272c 100644
--- a/UVtools.Core/Operations/OperationScripting.cs
+++ b/UVtools.Core/Operations/OperationScripting.cs
@@ -17,7 +17,7 @@ using UVtools.Core.Scripting;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationScripting : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs
index c3e68e3..71498f1 100644
--- a/UVtools.Core/Operations/OperationSolidify.cs
+++ b/UVtools.Core/Operations/OperationSolidify.cs
@@ -16,7 +16,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public sealed class OperationSolidify : Operation
{
#region Enums
diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs
index cfb9751..bb513fe 100644
--- a/UVtools.Core/Operations/OperationThreshold.cs
+++ b/UVtools.Core/Operations/OperationThreshold.cs
@@ -14,7 +14,7 @@ using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationThreshold : Operation
{
#region Members
diff --git a/UVtools.Core/Operations/OperationTimelapse.cs b/UVtools.Core/Operations/OperationTimelapse.cs
index 456ceb7..bf08db5 100644
--- a/UVtools.Core/Operations/OperationTimelapse.cs
+++ b/UVtools.Core/Operations/OperationTimelapse.cs
@@ -16,7 +16,7 @@ using UVtools.Core.Layers;
namespace UVtools.Core.Operations;
-[Serializable]
+
public class OperationTimelapse : Operation
{
#region Enums
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index e7d5120..b09b529 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, calibration, repair, conversion and manipulation</Description>
- <Version>3.8.2</Version>
+ <Version>3.8.3</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -84,7 +84,7 @@
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
- <PackageReference Include="System.Text.Json" Version="6.0.6" />
+ <PackageReference Include="System.Text.Json" Version="7.0.0" />
</ItemGroup>
<Target Name="PreparePackageReleaseNotesFromFile" BeforeTargets="GenerateNuspec">
diff --git a/UVtools.Installer/Code/HeatGeneratedFileList.wxs b/UVtools.Installer/Code/HeatGeneratedFileList.wxs
index f0dda83..fa4f7cb 100644
--- a/UVtools.Installer/Code/HeatGeneratedFileList.wxs
+++ b/UVtools.Installer/Code/HeatGeneratedFileList.wxs
@@ -320,8 +320,8 @@
<Component Id="cmpE35DDB3C925C83E74C7DAAE6FEA6C156" Guid="*">
<File Id="filEAEAE1CB9E8AC1FF00B05453ABEE2ADF" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore.dll" />
</Component>
- <Component Id="cmp512821EEEEE08D5AC2E2CEA01FDB1ABE" Guid="*">
- <File Id="fil44B372C655F54FBD2A2C09F0E183A476" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore_amd64_amd64_6.0.1022.47605.dll" />
+ <Component Id="cmp7FE735164884E23E3A78C7D52A84A02E" Guid="*">
+ <File Id="filF23EC789015B97CDD2EEC3398CD076F0" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore_amd64_amd64_6.0.1122.52304.dll" />
</Component>
<Component Id="cmpA5ABEAD31F33B1E5825EC6F159ABFB0F" Guid="*">
<File Id="fil1A6B0EBD5A5BF8DD0582A2665D3492F1" KeyPath="yes" Source="$(var.HarvestPath)\mscordbi.dll" />
@@ -1687,7 +1687,7 @@
<ComponentRef Id="cmp3E4F3E4686A9B425B5812E5171CCDEEF" />
<ComponentRef Id="cmp16861E24C897764CA922EC062BA74EC9" />
<ComponentRef Id="cmpE35DDB3C925C83E74C7DAAE6FEA6C156" />
- <ComponentRef Id="cmp512821EEEEE08D5AC2E2CEA01FDB1ABE" />
+ <ComponentRef Id="cmp7FE735164884E23E3A78C7D52A84A02E" />
<ComponentRef Id="cmpA5ABEAD31F33B1E5825EC6F159ABFB0F" />
<ComponentRef Id="cmp5D6845991403E371A015DF7EBDF71F13" />
<ComponentRef Id="cmp57B8361D25AD503DC733F4039E5A38B0" />
diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml
index b5fc822..e2517f0 100644
--- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml
@@ -7,8 +7,12 @@
x:Class="UVtools.WPF.Controls.Tools.ToolRepairLayersControl">
<StackPanel Orientation="Vertical" Spacing="10">
- <StackPanel Orientation="Horizontal" Spacing="20">
- <!-- Repair islands -->
+ <ToggleSwitch IsChecked="{Binding Operation.DetectIssues}"
+ OnContent="Re-detect the selected issues before repair"
+ OffContent="Uses and repair the previous detected issues"/>
+
+ <StackPanel Orientation="Horizontal" Spacing="20">
+ <!-- Repair islands -->
<CheckBox
IsChecked="{Binding Operation.RepairIslands}"
ToolTip.Tip="If enabled, repair will first attempt to eliminate islands smaller than the pixel area removal threshold, and then runs the “gap closure” technique."
diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
index 4863d5c..f779d2e 100644
--- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
@@ -33,7 +33,7 @@ public class ToolRepairLayersControl : ToolControl
SuctionCupsVentHole = UserSettings.Instance.LayerRepair.SuctionCupsVentHole,
GapClosingIterations = UserSettings.Instance.LayerRepair.ClosingIterations,
NoiseRemovalIterations = UserSettings.Instance.LayerRepair.OpeningIterations,
- IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration()
+ IssuesDetectionConfig = App.MainWindow.GetIssuesDetectionConfiguration()
};
public void SetFromUserSettings()
@@ -60,10 +60,10 @@ public class ToolRepairLayersControl : ToolControl
ParentWindow.IsCheckBox1Visible = true;
SetFromUserSettings();
- Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
+ Operation.IssuesDetectionConfig = App.MainWindow.GetIssuesDetectionConfiguration();
break;
case ToolWindow.Callbacks.Loaded:
- Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
+ Operation.IssuesDetectionConfig = App.MainWindow.GetIssuesDetectionConfiguration();
break;
case ToolWindow.Callbacks.Checkbox1:
ParentWindow.LayerRangeVisible = ParentWindow.IsCheckBox1Checked;
diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs
index 380525c..2a1273c 100644
--- a/UVtools.WPF/MainWindow.Issues.cs
+++ b/UVtools.WPF/MainWindow.Issues.cs
@@ -426,15 +426,11 @@ public partial class MainWindow
private async Task UpdateIslandsOverhangs(List<uint> whiteListLayers)
{
if (whiteListLayers.Count == 0) return;
- var islandConfig = GetIslandDetectionConfiguration();
- var overhangConfig = GetOverhangDetectionConfiguration();
- var resinTrapConfig = new ResinTrapDetectionConfiguration(false);
- var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- islandConfig.Enabled = true;
- islandConfig.WhiteListLayers = whiteListLayers;
- overhangConfig.Enabled = true;
- overhangConfig.WhiteListLayers = whiteListLayers;
+ var config = GetIssuesDetectionConfiguration(false);
+ config.IslandConfig.Enable();
+ config.IslandConfig.WhiteListLayers = whiteListLayers;
+ config.OverhangConfig.Enable();
+ config.OverhangConfig.WhiteListLayers = whiteListLayers;
IsGUIEnabled = false;
@@ -443,7 +439,7 @@ public partial class MainWindow
var issueList = SlicerFile.IssueManager.ToList();
issueList.RemoveAll(issue =>
- islandConfig.WhiteListLayers.Contains(issue.StartLayerIndex) && issue.Type is MainIssue.IssueType.Island or MainIssue.IssueType.Overhang);
+ config.IslandConfig.WhiteListLayers.Contains(issue.StartLayerIndex) && issue.Type is MainIssue.IssueType.Island or MainIssue.IssueType.Overhang);
/*foreach (var layerIndex in islandConfig.WhiteListLayers)
{
issueList.RemoveAll(issue =>
@@ -456,8 +452,7 @@ public partial class MainWindow
{
try
{
- var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig,
- touchingBoundConfig, printHeightConfig, false, Progress);
+ var issues = SlicerFile.IssueManager.DetectIssues(config, Progress);
issues.RemoveAll(issue => issue.Type is not MainIssue.IssueType.Island and not MainIssue.IssueType.Overhang); // Remove all non islands and overhangs
return issues;
@@ -632,23 +627,11 @@ public partial class MainWindow
return;
}
- await ComputeIssues(
- GetIslandDetectionConfiguration(),
- GetOverhangDetectionConfiguration(),
- GetResinTrapDetectionConfiguration(),
- GetTouchingBoundsDetectionConfiguration(),
- GetPrintHeightDetectionConfiguration(),
- Settings.Issues.ComputeEmptyLayers);
+ await ComputeIssues(GetIssuesDetectionConfiguration());
}
- private async Task ComputeIssues(IslandDetectionConfiguration islandConfig = null,
- OverhangDetectionConfiguration overhangConfig = null,
- ResinTrapDetectionConfiguration resinTrapConfig = null,
- TouchingBoundDetectionConfiguration touchingBoundConfig = null,
- PrintHeightDetectionConfiguration printHeightConfig = null,
- bool emptyLayersConfig = true)
+ private async Task ComputeIssues(IssuesDetectionConfiguration config)
{
-
SlicerFile.IssueManager.Clear();
IsGUIEnabled = false;
ShowProgressWindow("Computing Issues");
@@ -657,8 +640,7 @@ public partial class MainWindow
{
try
{
- var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig,
- printHeightConfig, emptyLayersConfig, Progress);
+ var issues = SlicerFile.IssueManager.DetectIssues(config, Progress);
switch (Settings.Issues.DataGridOrderBy)
{
@@ -843,6 +825,18 @@ public partial class MainWindow
}
}
+ public IssuesDetectionConfiguration GetIssuesDetectionConfiguration(bool enable = true)
+ {
+ return new IssuesDetectionConfiguration(
+ GetIslandDetectionConfiguration(enable),
+ GetOverhangDetectionConfiguration(enable),
+ GetResinTrapDetectionConfiguration(enable),
+ GetTouchingBoundsDetectionConfiguration(enable),
+ GetPrintHeightDetectionConfiguration(enable),
+ GetEmptyLayerDetectionConfiguration(enable)
+ );
+ }
+
public IslandDetectionConfiguration GetIslandDetectionConfiguration(bool enable)
@@ -916,6 +910,15 @@ public partial class MainWindow
}
public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration() => GetPrintHeightDetectionConfiguration(Settings.Issues.ComputePrintHeight);
+ public EmptyLayerDetectionConfiguration GetEmptyLayerDetectionConfiguration(bool enable)
+ {
+ return new()
+ {
+ Enabled = enable,
+ };
+ }
+ public EmptyLayerDetectionConfiguration GetEmptyLayerDetectionConfiguration() => GetEmptyLayerDetectionConfiguration(Settings.Issues.ComputeEmptyLayers);
+
#endregion
} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 0363e7a..42528e1 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -894,7 +894,7 @@
Binding="{Binding LayerInfoStr}"
Width="Auto" />
<DataGridTextColumn Header="Area"
- Binding="{Binding Area, StringFormat={}{0:F0}}"
+ Binding="{Binding Area, StringFormat={}{0:N0}}"
Width="Auto" />
</DataGrid.Columns>
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index 69a8ec1..be0e5d1 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -26,7 +26,6 @@ using UVtools.AvaloniaControls;
using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-using UVtools.Core.Layers;
using UVtools.Core.Managers;
using UVtools.Core.Network;
using UVtools.Core.Objects;
@@ -1747,13 +1746,10 @@ public partial class MainWindow : WindowEx
}
else
{
- await ComputeIssues(
- GetIslandDetectionConfiguration(false),
- GetOverhangDetectionConfiguration(false),
- GetResinTrapDetectionConfiguration(false),
- GetTouchingBoundsDetectionConfiguration(false),
- GetPrintHeightDetectionConfiguration(true),
- true);
+ var config = GetIssuesDetectionConfiguration(false);
+ config.PrintHeightConfig.Enable();
+ config.EmptyLayerConfig.Enable();
+ await ComputeIssues(config);
if (SlicerFile.IssueManager.Count > 0)
{
SelectedTabItem = TabIssues;
@@ -2144,17 +2140,13 @@ public partial class MainWindow : WindowEx
case OperationRepairLayers operation:
if (SlicerFile.IssueManager.Count == 0)
{
- var islandConfig = GetIslandDetectionConfiguration();
- islandConfig.Enabled = operation.RepairIslands && operation.RemoveIslandsBelowEqualPixelCount > 0;
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var resinTrapConfig = GetResinTrapDetectionConfiguration();
- resinTrapConfig.Enabled = operation.RepairResinTraps;
- var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
-
- if (islandConfig.Enabled || resinTrapConfig.Enabled)
+ var config = GetIssuesDetectionConfiguration(false);
+ config.IslandConfig.Enabled = operation.RepairIslands && operation.RemoveIslandsBelowEqualPixelCount > 0;
+ config.ResinTrapConfig.Enabled = operation.RepairResinTraps;
+
+ if (config.IslandConfig.Enabled || config.ResinTrapConfig.Enabled)
{
- await ComputeIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig, printHeightConfig, Settings.Issues.ComputeEmptyLayers);
+ await ComputeIssues(config);
}
}
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 67bb7dd..a7f4afa 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -289,6 +289,7 @@ public class AppVersionChecker : BindableBase
var newDirectoryName = Regex.Replace(di.Name, $@"({About.Software}.*)(v\d+.\d+.\d+)", $@"$1v{_version}", RegexOptions.IgnoreCase);
if (di.Name != newDirectoryName)
{
+ await stream.WriteLineAsync();
await stream.WriteLineAsync("echo '- Directory is able to rename version name'");
applicationPath = Path.Combine(di.Parent?.FullName!, newDirectoryName);
}
diff --git a/UVtools.WPF/Structures/Color.cs b/UVtools.WPF/Structures/Color.cs
index af6e38d..43565a6 100644
--- a/UVtools.WPF/Structures/Color.cs
+++ b/UVtools.WPF/Structures/Color.cs
@@ -11,7 +11,7 @@ using Avalonia.Media;
namespace UVtools.WPF.Structures;
-[Serializable]
+
public class Color
{
public byte A;
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
index e097b67..30afe34 100644
--- a/UVtools.WPF/Structures/OperationProfiles.cs
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -16,7 +16,7 @@ using UVtools.Core.Operations;
namespace UVtools.WPF.Structures;
-[Serializable]
+
public class OperationProfiles //: IList<Operation>
{
#region Properties
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 27ad74a..7a4a941 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>3.8.2</Version>
+ <Version>3.8.3</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index a77dab2..1d5da7f 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -25,7 +25,7 @@ using Color=UVtools.WPF.Structures.Color;
namespace UVtools.WPF;
-[Serializable]
+
public sealed class UserSettings : BindableBase
{
#region Constants
@@ -35,7 +35,7 @@ public sealed class UserSettings : BindableBase
#region Sub classes
#region General
- [Serializable]
+
public sealed class GeneralUserSettings : BindableBase
{
private App.ApplicationTheme _theme = App.ApplicationTheme.FluentLight;
@@ -259,7 +259,7 @@ public sealed class UserSettings : BindableBase
#endregion
#region Layer Preview
- [Serializable]
+
public sealed class LayerPreviewUserSettings : BindableBase
{
private Color _tooltipOverlayBackgroundColor = new(210, 226, 223, 215);
@@ -868,7 +868,7 @@ public sealed class UserSettings : BindableBase
#endregion
#region Issues
- [Serializable]
+
public sealed class IssuesUserSettings : BindableBase
{
private bool _computeIssuesOnLoad;
@@ -1161,7 +1161,7 @@ public sealed class UserSettings : BindableBase
#endregion
#region Pixel Editor
- [Serializable]
+
public sealed class PixelEditorUserSettings : BindableBase
{
private Color _addPixelColor = new(255, 144, 238, 144);
@@ -1349,7 +1349,7 @@ public sealed class UserSettings : BindableBase
#endregion
#region Layer Repair
- [Serializable]
+
public sealed class LayerRepairUserSettings : BindableBase
{
private bool _repairIslands = true;
@@ -1439,7 +1439,7 @@ public sealed class UserSettings : BindableBase
#region Tools
- [Serializable]
+
public sealed class ToolsUserSettings : BindableBase
{
private bool _expandDescriptions = true;
@@ -1483,7 +1483,7 @@ public sealed class UserSettings : BindableBase
#region Automations
- [Serializable]
+
public sealed class AutomationsUserSettings : BindableBase
{
private bool _saveFileAfterModifications = true;
@@ -1525,7 +1525,7 @@ public sealed class UserSettings : BindableBase
#region Network
- [Serializable]
+
public sealed class NetworkUserSettings : BindableBase
{
private RangeObservableCollection<RemotePrinter> _remotePrinters = new();
diff --git a/build/libcvextern.sh b/build/libcvextern.sh
index 1f95e62..b2deade 100644
--- a/build/libcvextern.sh
+++ b/build/libcvextern.sh
@@ -46,7 +46,7 @@ done
echo "Script to build libcvextern.so|dylib on $(uname -a) $arch"
if testcmd ldconfig; then
- if [ -z "$(ldconfig -p | grep libpng)" -o -z "$(ldconfig -p | grep libgdiplus)" -o -z "$(ldconfig -p | grep libavcodec)" -o -z "$(command -v git)" -o -z "$(command -v cmake)" ]; then
+ if [ -z "$(ldconfig -p | grep libpng)" -o -z "$(ldconfig -p | grep libgdiplus)" -o -z "$(ldconfig -p | grep libavcodec)" -o -z "$(command -v git)" -o -z "$(command -v cmake)" -o -z "$(command -v dotnet)" ]; then
installDependencies=true
fi
fi
@@ -67,14 +67,14 @@ if [ "${OSTYPE:0:6}" == "darwin" ]; then
[ -z "$(command -v cmake)" ] && brew install cmake
[ -z "$(command -v mono)" ] && brew install mono
[ -z "$(command -v dotnet)" ] && brew install --cask dotnet-sdk
-elif testcmd apt-get; then
+elif testcmd apt; then
osVariant="debian"
if [ "$installDependencies" == true ]; then
- sudo apt-get update
- sudo apt-get -y install git build-essential libgtk-3-dev libgstreamer1.0-dev libavcodec-dev libswscale-dev libavformat-dev libdc1394-dev libv4l-dev cmake-curses-gui ocl-icd-dev freeglut3-dev libgeotiff-dev libusb-1.0-0-dev
- sudo apt-get install -y apt-transport-https
- sudo apt-get update
- sudo apt-get install -y dotnet-sdk-6.0
+ sudo apt update
+ sudo apt -y install git build-essential libgtk-3-dev libgstreamer1.0-dev libavcodec-dev libswscale-dev libavformat-dev libdc1394-dev libv4l-dev cmake-curses-gui ocl-icd-dev freeglut3-dev libgeotiff-dev libusb-1.0-0-dev
+ sudo apt install -y apt-transport-https
+ sudo apt update
+ sudo apt install -y dotnet-sdk-6.0
fi
elif testcmd pacman; then
osVariant="arch"
diff --git a/build/platforms/linux-x64/libcvextern.zip b/build/platforms/linux-x64/libcvextern.zip
index 3e1915f..49c2722 100644
--- a/build/platforms/linux-x64/libcvextern.zip
+++ b/build/platforms/linux-x64/libcvextern.zip
Binary files differ
diff --git a/build/platforms/linux-x64/libcvextern22.04.zip b/build/platforms/linux-x64/libcvextern22.04.zip
new file mode 100644
index 0000000..3e1915f
--- /dev/null
+++ b/build/platforms/linux-x64/libcvextern22.04.zip
Binary files differ
diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml
index feb0c97..9576418 100644
--- a/documentation/UVtools.Core.xml
+++ b/documentation/UVtools.Core.xml
@@ -3456,27 +3456,27 @@
<param name="recalculateZPos">True to recalculate z position of each layer (requires <paramref name="callRebuildOnEnd"/> = true), otherwise false</param>
<param name="property">Property name to change for each layer, use null to update all properties (requires <paramref name="callRebuildOnEnd"/> = true)</param>
</member>
- <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersXToPixels(System.UInt16,System.UInt32)">
+ <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersXToPixels(System.Single,System.UInt32)">
<summary>
Converts millimeters to pixels given the current resolution and display size
</summary>
- <param name="mm">Millimeters to convert</param>
+ <param name="millimeters">Millimeters to convert</param>
<param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
<returns>Pixels</returns>
</member>
- <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersYToPixels(System.UInt16,System.UInt32)">
+ <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersYToPixels(System.Single,System.UInt32)">
<summary>
Converts millimeters to pixels given the current resolution and display size
</summary>
- <param name="mm">Millimeters to convert</param>
+ <param name="millimeters">Millimeters to convert</param>
<param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
<returns>Pixels</returns>
</member>
- <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersToPixels(System.UInt16,System.UInt32)">
+ <member name="M:UVtools.Core.FileFormats.FileFormat.MillimetersToPixels(System.Single,System.UInt32)">
<summary>
Converts millimeters to pixels given the current resolution and display size
</summary>
- <param name="mm">Millimeters to convert</param>
+ <param name="millimeters">Millimeters to convert</param>
<param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
<returns>Pixels</returns>
</member>
@@ -3636,6 +3636,15 @@
<param name="layerIndex">Layer index to check</param>
<returns></returns>
</member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.TryParseLayerIndexRange(System.String,System.UInt32@,System.UInt32@)">
+ <summary>
+ Try to parse starting and ending layer index from a string
+ </summary>
+ <param name="value">String value to parse, in start:end format</param>
+ <param name="layerIndexStart">Parsed starting layer index</param>
+ <param name="layerIndexEnd">Parsed ending layer index</param>
+ <returns></returns>
+ </member>
<member name="M:UVtools.Core.FileFormats.FileFormat.SanitizeLayerIndex(System.UInt32@)">
<summary>
Constrains a layer index to be inside the range between 0 and <see cref="P:UVtools.Core.FileFormats.FileFormat.LastLayerIndex"/>
@@ -5065,7 +5074,7 @@
<param name="layers">Layer collection</param>
<returns></returns>
</member>
- <member name="P:UVtools.Core.Layers.IslandDetectionConfiguration.Enabled">
+ <member name="P:UVtools.Core.Layers.DetectionConfiguration.Enabled">
<summary>
Gets or sets if the detection is enabled
</summary>
@@ -5127,11 +5136,6 @@
Overhang configuration
</summary>
</member>
- <member name="P:UVtools.Core.Layers.OverhangDetectionConfiguration.Enabled">
- <summary>
- Gets or sets if the detection is enabled
- </summary>
- </member>
<member name="P:UVtools.Core.Layers.OverhangDetectionConfiguration.WhiteListLayers">
<summary>
Gets or sets a list of layers to check for overhangs, absent layers will not be checked.
@@ -5154,11 +5158,6 @@
The survived pixels are potential overhangs.
</summary>
</member>
- <member name="P:UVtools.Core.Layers.ResinTrapDetectionConfiguration.Enabled">
- <summary>
- Gets or sets if the detection is enabled
- </summary>
- </member>
<member name="P:UVtools.Core.Layers.ResinTrapDetectionConfiguration.StartLayerIndex">
<summary>
Gets or sets the starting layer index for the detection which will also be considered a drain layer.
@@ -5201,11 +5200,6 @@
Required minimum height (in mm) to be considered a suction cup
</summary>
</member>
- <member name="P:UVtools.Core.Layers.TouchingBoundDetectionConfiguration.Enabled">
- <summary>
- Gets if the detection is enabled
- </summary>
- </member>
<member name="P:UVtools.Core.Layers.TouchingBoundDetectionConfiguration.MinimumPixelBrightness">
<summary>
Gets the minimum pixel brightness to be a touching bound
@@ -5231,14 +5225,27 @@
Gets or sets the margin in pixels from bottom edge to check for touching white pixels
</summary>
</member>
- <member name="P:UVtools.Core.Layers.PrintHeightDetectionConfiguration.Enabled">
+ <member name="P:UVtools.Core.Layers.PrintHeightDetectionConfiguration.Offset">
<summary>
- Gets if the detection is enabled
+ Get the offset from top to sum to printer max Z height
</summary>
</member>
- <member name="P:UVtools.Core.Layers.PrintHeightDetectionConfiguration.Offset">
+ <member name="P:UVtools.Core.Layers.EmptyLayerDetectionConfiguration.IgnoreStartingEmptyLayers">
<summary>
- Get the offset from top to sum to printer max Z height
+ <para>Gets or sets to ignore the starting empty layers.</para>
+ <para>True to ignore starting empty layers, otherwise false.</para>
+ </summary>
+ </member>
+ <member name="P:UVtools.Core.Layers.EmptyLayerDetectionConfiguration.IgnoreLooseEmptyLayers">
+ <summary>
+ <para>Gets or sets to ignore the loose empty layers that are not on start nor in end.</para>
+ <para>True to ignore loose empty layers, otherwise false.</para>
+ </summary>
+ </member>
+ <member name="P:UVtools.Core.Layers.EmptyLayerDetectionConfiguration.IgnoreEndingEmptyLayers">
+ <summary>
+ <para>Gets or sets to ignore the ending empty layers.</para>
+ <para>True to ignore ending empty layers, otherwise false.</para>
</summary>
</member>
<member name="P:UVtools.Core.Layers.MainIssue.Type">
@@ -5291,6 +5298,11 @@
Gets the area of the issue
</summary>
</member>
+ <member name="P:UVtools.Core.Layers.MainIssue.AreaChar">
+ <summary>
+ Gets the area character, either ² or ³
+ </summary>
+ </member>
<member name="P:UVtools.Core.Layers.MainIssue.Childs">
<summary>
Gets all issues inside this main issue
@@ -6510,6 +6522,11 @@
True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false
</summary>
</member>
+ <member name="P:UVtools.Core.Operations.OperationRepairLayers.DetectIssues">
+ <summary>
+ IF true it will re-detect the selected issues before repair, otherwise uses and repair the previous detected issues
+ </summary>
+ </member>
<member name="P:UVtools.Core.Operations.OperationSolidify.MinimumArea">
<summary>
Gets the minimum required area to solidify it