diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-11-10 07:09:00 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-11-10 07:09:00 +0300 |
commit | 8e4995a6a5f35bbee9fa54108b2743fd7fe59f18 (patch) | |
tree | 9f68381c2a9b66d2512aef8b658bca3f336d7252 | |
parent | 051f11bb6e723e9ccc624abdaa69e5d8750ffa01 (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
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:** @@ -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 Binary files differindex 3e1915f..49c2722 100644 --- a/build/platforms/linux-x64/libcvextern.zip +++ b/build/platforms/linux-x64/libcvextern.zip diff --git a/build/platforms/linux-x64/libcvextern22.04.zip b/build/platforms/linux-x64/libcvextern22.04.zip Binary files differnew file mode 100644 index 0000000..3e1915f --- /dev/null +++ b/build/platforms/linux-x64/libcvextern22.04.zip 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 |