From 0c63caa547a0aa15ac5326158eed232823aff992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sat, 5 Nov 2022 23:07:16 +0000 Subject: v3.8.2 - **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 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) --- CHANGELOG.md | 24 + README.md | 18 +- RELEASE_NOTES.md | 33 +- UVtools.Cmd/Program.cs | 1 + UVtools.Cmd/Symbols/ExtractCommand.cs | 2 +- UVtools.Cmd/Symbols/SetThumbnailCommand.cs | 148 ++++++ UVtools.Cmd/UVtools.Cmd.csproj | 2 +- UVtools.Core/Extensions/EmguExtensions.cs | 8 +- UVtools.Core/FileFormats/FileFormat.cs | 156 +++++- UVtools.Core/Gerber/GerberDocument.cs | 9 +- UVtools.Core/Layers/Layer.cs | 36 +- UVtools.Core/Operations/Operation.cs | 7 +- .../Operations/OperationLayerExportHeatMap.cs | 44 +- .../Operations/OperationLayerExportMesh.cs | 149 ++++-- UVtools.Core/UVtools.Core.csproj | 7 +- UVtools.Core/Voxel/GridCube.cs | 17 + UVtools.Core/Voxel/MarchingCubes.cs | 540 +++++++++++++++++++++ UVtools.Core/Voxel/MarchingCubesTable.cs | 281 +++++++++++ UVtools.WPF/App.axaml.cs | 18 +- UVtools.WPF/Controls/WindowEx.cs | 6 +- UVtools.WPF/MainWindow.Information.cs | 164 ++++++- UVtools.WPF/MainWindow.LayerPreview.cs | 5 +- UVtools.WPF/MainWindow.Suggestions.cs | 16 +- UVtools.WPF/MainWindow.axaml | 72 ++- UVtools.WPF/MainWindow.axaml.cs | 11 +- UVtools.WPF/Program.cs | 20 +- UVtools.WPF/Structures/AppVersionChecker.cs | 66 ++- UVtools.WPF/UVtools.WPF.csproj | 2 +- documentation/UVtools.Core.xml | 68 ++- 29 files changed, 1751 insertions(+), 179 deletions(-) create mode 100644 UVtools.Cmd/Symbols/SetThumbnailCommand.cs create mode 100644 UVtools.Core/Voxel/GridCube.cs create mode 100644 UVtools.Core/Voxel/MarchingCubes.cs create mode 100644 UVtools.Core/Voxel/MarchingCubesTable.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5772c..be4f925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 05/11/2022 - v3.8.2 + +- **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 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) + ## 02/11/2022 - v3.8.1 - **Tools:** diff --git a/README.md b/README.md index 9354251..0f821dd 100644 --- a/README.md +++ b/README.md @@ -190,15 +190,15 @@ Options: -?, -h, --help Show help and usage information Commands: - run Run operations and/or scripts - convert Convert input file into a output file format by a known type or - extension [] - extract Extract file contents to a folder [] - copy-parameters Copy print parameters from one file to another - print-properties Prints available properties - print-layers Prints layer(s) properties - print-gcode Prints the gcode of the file if available - print-machines Prints machine settings + run Run operations and/or scripts + convert Convert input file into a output file format by a known type or extension [] + extract Extract file contents to a folder [] + copy-parameters Copy print parameters from one file to another + set-preview, set-thumbnail Sets and replace thumbnail(s) in the file [default: :heatmap] + print-properties Prints available properties + print-layers Prints layer(s) properties + print-gcode 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 a355dcb..5099425 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,11 +1,22 @@ -- **Tools:** - - **PCB Exposure:** - - (Add) Allow to scale the drawing sizes per gerber file - - (Add) Allow to invert the drawing polarity per gerber file (#592) - - (Add) Allow to drag and drop files into "Add files" button and grid header - - (Improvement) Do not add empty layers when usable to draw or when draw a all black image - - (Improvement) "Merge all gerbers into one layer" will now draw all gerber into one image instead of perform the Max(of all gerbers pixels), allowing to subtract areas as they are on gerbers - - **Import layers:** Fix error when trying to insert layers - - **Export layers images:** Better compression of contours for SVG export, resulting in smooth curves, better visuals and lower file size -- (Add) Outline: Triangulate -- (Remove) Avalonia.Diagnostics dependency in release mode +- **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 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) + diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs index d332156..a098f49 100644 --- a/UVtools.Cmd/Program.cs +++ b/UVtools.Cmd/Program.cs @@ -42,6 +42,7 @@ internal class Program ConvertCommand.CreateCommand(), ExtractCommand.CreateCommand(), CopyParametersCommand.CreateCommand(), + SetThumbnailCommand.CreateCommand(), PrintPropertiesCommand.CreateCommand(), PrintLayersCommand.CreateCommand(), diff --git a/UVtools.Cmd/Symbols/ExtractCommand.cs b/UVtools.Cmd/Symbols/ExtractCommand.cs index 8c48d2d..53b3735 100644 --- a/UVtools.Cmd/Symbols/ExtractCommand.cs +++ b/UVtools.Cmd/Symbols/ExtractCommand.cs @@ -41,7 +41,7 @@ internal static class ExtractCommand Program.ProgressBarWork($"Extracting to {Path.GetFileName(path)}", () => { - slicerFile.Extract(path); + slicerFile.Extract(path, progress:Program.Progress); }); }, GlobalArguments.InputFileArgument, GlobalArguments.OutputDirectoryArgument, noOverwriteOption); diff --git a/UVtools.Cmd/Symbols/SetThumbnailCommand.cs b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs new file mode 100644 index 0000000..9143ada --- /dev/null +++ b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs @@ -0,0 +1,148 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +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; + +internal static class SetThumbnailCommand +{ + private const string HeatmapArg = ":heatmap"; + private const string RandomLayerArg = ":random-layer"; + internal static Command CreateCommand() + { + var sourceArgument = new Argument($"file path|layer index|{RandomLayerArg}|{HeatmapArg}", () => HeatmapArg, "Choose from a file, layer index, random layer or generate a heatmap"); + var thumbnailIndexesOption = new Option>("-i", "Select the thumbnail index(es) to set"); + + var command = new Command("set-thumbnail", "Sets and replace thumbnail(s) in the file") + { + GlobalArguments.InputFileArgument, + sourceArgument, + thumbnailIndexesOption, + }; + + command.AddAlias("set-preview"); + + command.SetHandler(async (inputFile, source, thumbnailIndexes) => + { + if (string.IsNullOrWhiteSpace(source)) + { + Program.WriteLineError("Invalid empty source argument."); + } + + bool result = false; + + if (string.Equals(source, HeatmapArg, StringComparison.OrdinalIgnoreCase)) + { + var slicerFile = Program.OpenInputFile(inputFile); + using var mat = await Program.ProgressBarWork($"Generating a heatmap from layers 0 through {slicerFile.LastLayerIndex}", + () => slicerFile.GenerateHeatmapAsync(slicerFile.GetBoundingRectangle(50, 100, Program.Progress), Program.Progress)); + + CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr); + + + if (thumbnailIndexes.Any()) + { + foreach (var thumbnailIndex in thumbnailIndexes) + { + result = slicerFile.SetThumbnail(thumbnailIndex, mat); + } + } + else + { + result = slicerFile.SetThumbnails(mat) > 0; + } + + if(result) Program.SaveFile(slicerFile); + return; + } + + if (string.Equals(source, RandomLayerArg, StringComparison.OrdinalIgnoreCase)) + { + var slicerFile = Program.OpenInputFile(inputFile); + if(slicerFile.LayerCount == 0) Program.WriteLineError("The file have no valid layers"); + + + using var matRoi = slicerFile[Random.Shared.Next((int) slicerFile.LayerCount)].GetLayerMatBoundingRectangle(50, 100); + CvInvoke.CvtColor(matRoi.RoiMat, matRoi.RoiMat, ColorConversion.Gray2Bgr); + + if (thumbnailIndexes.Any()) + { + foreach (var thumbnailIndex in thumbnailIndexes) + { + result = slicerFile.SetThumbnail(thumbnailIndex, matRoi.RoiMat); + } + } + else + { + result = slicerFile.SetThumbnails(matRoi.RoiMat) > 0; + } + + if (result) Program.SaveFile(slicerFile); + return; + } + + if (uint.TryParse(source, out var layerIndex)) + { + var slicerFile = Program.OpenInputFile(inputFile); + if (slicerFile.LayerCount == 0) Program.WriteLineError("The file have no valid layers"); + slicerFile.SanitizeLayerIndex(ref layerIndex); + + using var matRoi = slicerFile[layerIndex].GetLayerMatBoundingRectangle(50, 100); + CvInvoke.CvtColor(matRoi.RoiMat, matRoi.RoiMat, ColorConversion.Gray2Bgr); + + if (thumbnailIndexes.Any()) + { + foreach (var thumbnailIndex in thumbnailIndexes) + { + result = slicerFile.SetThumbnail(thumbnailIndex, matRoi.RoiMat); + } + } + else + { + result = slicerFile.SetThumbnails(matRoi.RoiMat) > 0; + } + + if (result) Program.SaveFile(slicerFile); + return; + } + + if (File.Exists(source)) + { + var slicerFile = Program.OpenInputFile(inputFile); + + if (thumbnailIndexes.Any()) + { + foreach (var thumbnailIndex in thumbnailIndexes) + { + result = slicerFile.SetThumbnail(thumbnailIndex, source); + } + } + else + { + result = slicerFile.SetThumbnails(source) > 0; + } + + if (result) Program.SaveFile(slicerFile); + return; + } + + Program.WriteLineError($"'{source}' is not a file nor layer index nor {RandomLayerArg} nor {HeatmapArg}"); + + + }, GlobalArguments.InputFileArgument, sourceArgument, thumbnailIndexesOption); + + return command; + } +} \ No newline at end of file diff --git a/UVtools.Cmd/UVtools.Cmd.csproj b/UVtools.Cmd/UVtools.Cmd.csproj index 7dcf1df..ecbec54 100644 --- a/UVtools.Cmd/UVtools.Cmd.csproj +++ b/UVtools.Cmd/UVtools.Cmd.csproj @@ -5,7 +5,7 @@ net6.0 UVtoolsCmd UVtools.ico - 1.0.3 + 1.0.4 Tiago Conceição, sn4k3 PTRTECH LICENSE diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 6dd3694..fcc4e50 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -490,17 +490,17 @@ public static class EmguExtensions #region Create methods - public static Mat CreateMask(this Mat src, VectorOfVectorOfPoint contours) + public static Mat CreateMask(this Mat src, VectorOfVectorOfPoint contours, Point offset = default) { var mask = src.NewBlank(); - CvInvoke.DrawContours(mask, contours, -1, WhiteColor, -1); + CvInvoke.DrawContours(mask, contours, -1, WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, offset); return mask; } - public static Mat CreateMask(this Mat src, Point[][] contours) + public static Mat CreateMask(this Mat src, Point[][] contours, Point offset = default) { using var vec = new VectorOfVectorOfPoint(contours); - return src.CreateMask(vec); + return src.CreateMask(vec, offset); } public static Mat CropByBounds(this Mat src, bool cloneInsteadRoi = false) diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 742b023..b7e0eba 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -1510,6 +1510,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable public abstract uint ResolutionY { get; set; } + /// + /// Gets an rectangle that starts at 0,0 and goes up to + /// + public Rectangle ResolutionRectangle => new(Point.Empty, Resolution); + /// /// Gets the display total number of pixels ( * ) /// @@ -3331,10 +3337,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable /// - public void SetThumbnails(Mat?[] images) + public byte SetThumbnails(Mat?[] images) { - if (images.Length == 0) return; + if (images.Length == 0) return 0; byte imageIndex = 0; + byte changed = 0; for (int i = 0; i < ThumbnailsCount; i++) { var image = images[Math.Min(imageIndex++, images.Length - 1)]; @@ -3350,44 +3357,74 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable /// Sets all thumbnails the same image /// /// Image to set - public void SetThumbnails(Mat image) + public byte SetThumbnails(Mat image) + { + return SetThumbnails(new[] {image}); + } + + /// + /// Sets all thumbnails from a disk file + /// + /// + public byte SetThumbnails(string filePath) + { + if (!File.Exists(filePath)) return 0; + using var image = CvInvoke.Imread(filePath, ImreadModes.Color); + return SetThumbnails(image); + } + + /// + /// Sets a thumbnail from mat + /// + /// Thumbnail index + /// + public bool SetThumbnail(int index, Mat image) { - if (image.IsEmpty) return; - for (var i = 0; i < ThumbnailsCount; i++) + if (index >= Thumbnails.Length) return false; + Thumbnails[index] = image.Clone(); + if (Thumbnails[index]!.Size != ThumbnailsOriginalSize![index]) { - Thumbnails[i] = image.Clone(); - if (ThumbnailsOriginalSize is null || i >= ThumbnailsOriginalSize.Length) continue; - if (Thumbnails[i]!.Size != ThumbnailsOriginalSize[i]) - { - CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]); - } + CvInvoke.Resize(Thumbnails[index], Thumbnails[index], ThumbnailsOriginalSize[index]); } RaisePropertyChanged(nameof(Thumbnails)); + RequireFullEncode = true; + return true; } + /// /// Sets a thumbnail from a disk file /// /// Thumbnail index /// - public void SetThumbnail(int index, string filePath) + public bool SetThumbnail(int index, string filePath) { - if (index >= Thumbnails.Length) return; - Thumbnails[index] = CvInvoke.Imread(filePath, ImreadModes.AnyColor); + if (!File.Exists(filePath)) return false; + if (index >= Thumbnails.Length) return false; + Thumbnails[index] = CvInvoke.Imread(filePath, ImreadModes.Color); if (Thumbnails[index]!.Size != ThumbnailsOriginalSize![index]) { CvInvoke.Resize(Thumbnails[index], Thumbnails[index], ThumbnailsOriginalSize[index]); } RaisePropertyChanged(nameof(Thumbnails)); + RequireFullEncode = true; + return true; } /// @@ -5200,6 +5237,12 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable new(DisplayToPixelPositionX(x), DisplayToPixelPositionY(y)); public Point DisplayToPixelPosition(PointF point) => new(DisplayToPixelPositionX(point.X), DisplayToPixelPositionY(point.Y)); + public bool SanitizeBoundingRectangle(ref Rectangle rectangle) + { + var oldRectangle = rectangle; + rectangle = Rectangle.Intersect(rectangle, ResolutionRectangle); + return oldRectangle != rectangle; + } public Rectangle GetBoundingRectangle(OperationProgress? progress = null) { @@ -5250,6 +5293,18 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable GetBoundingRectangle(margin, margin, progress); + public Rectangle GetBoundingRectangle(Size margin, OperationProgress? progress = null) => GetBoundingRectangle(margin.Width, margin.Height, progress); + /// /// Creates a empty mat of file size and create a dummy pixel to prevent a empty layer detection @@ -5628,6 +5683,18 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable + /// Constrains a layer index to be inside the range between 0 and + /// + /// Layer index to sanitize + /// True if sanitized, otherwise false + public bool SanitizeLayerIndex(ref uint layerIndex) + { + var originalValue = layerIndex; + layerIndex = Math.Min(layerIndex, LastLayerIndex); + return originalValue != layerIndex; + } + /// /// Re-assign layer indexes and parent /// @@ -6171,4 +6238,65 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable + /// Generates a heatmap based on a stack of layers + /// + /// Layer index to start from + /// Layer index to end on + /// Region of interest + /// + /// Heatmap grayscale Mat + public Mat GenerateHeatmap(uint layerIndexStart = 0, uint layerIndexEnd = uint.MaxValue, Rectangle roi = default, OperationProgress? progress = null) + { + SanitizeLayerIndex(ref layerIndexEnd); + + progress ??= new OperationProgress(); + progress.Title = $"Generating a heatmap from layers {layerIndexStart} through {layerIndexEnd}"; + progress.ItemName = "layers"; + + if (roi.IsEmpty) roi = ResolutionRectangle; + + var resultMat = EmguExtensions.InitMat(roi.Size, 1, DepthType.Cv32S); + var layerRange = GetDistinctLayersByPositionZ(layerIndexStart, layerIndexEnd).ToArray(); + + progress.ItemCount = (uint)layerRange.Length; + + Parallel.ForEach(layerRange, CoreSettings.GetParallelOptions(progress), layer => + { + using var mat = GetMergedMatForSequentialPositionedLayers(layer.Index); + using var mat32Roi = mat.Roi(roi); + + mat32Roi.ConvertTo(mat32Roi, DepthType.Cv32S); + + lock (progress.Mutex) + { + CvInvoke.Add(resultMat, mat32Roi, resultMat); + progress++; + } + }); + + + resultMat.ConvertTo(resultMat, DepthType.Cv8U, 1.0 / layerRange.Length); + + return resultMat; + } + + /// + /// Generates a heatmap based on a stack of layers + /// + /// Region of interest + /// + /// Heatmap grayscale Mat + public Mat GenerateHeatmap(Rectangle roi, OperationProgress? progress = null) => GenerateHeatmap(0, uint.MaxValue, roi, progress); + + public Task GenerateHeatmapAsync(uint layerIndexStart = 0, uint layerIndexEnd = uint.MaxValue, Rectangle roi = default, OperationProgress? progress = null) + => Task.Run(() => GenerateHeatmap(layerIndexStart, layerIndexEnd, roi, progress)); + + public Task GenerateHeatmapAsync(Rectangle roi, OperationProgress? progress = null) + => Task.Run(() => GenerateHeatmap(0, uint.MaxValue, roi, progress)); + + #endregion } \ No newline at end of file diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberDocument.cs index 271d371..3e4e04f 100644 --- a/UVtools.Core/Gerber/GerberDocument.cs +++ b/UVtools.Core/Gerber/GerberDocument.cs @@ -105,6 +105,12 @@ public class GerberDocument if (line == string.Empty) continue; if (line.StartsWith("M02")) break; + if (currentMacro is not null && line[0] == '%') + { + currentMacro = null; + continue; + } + var accumulatedLine = line; while (!accumulatedLine.Contains('*') && (line = file.ReadLine()) is not null) { @@ -430,8 +436,7 @@ public class GerberDocument } else if (d == 3) { - currentAperture.DrawFlashD3(mat, new PointF((float)nowX, (float)nowY), - document.PolarityColor, enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + currentAperture.DrawFlashD3(mat, new PointF((float)nowX, (float)nowY), document.PolarityColor, enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); //CvInvoke.Imshow("G37", mat); //CvInvoke.WaitKey(); } diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index c1769d8..17e7706 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -832,6 +832,28 @@ public class Layer : BindableBase, IEquatable, IEquatable /// public MatRoi GetLayerMat(Rectangle roi) => new(LayerMat, roi); + /// + /// Gets the layer mat with bounding rectangle mat + /// + /// Margin from bounding rectangle + /// + public MatRoi GetLayerMatBoundingRectangle(int margin) => new(LayerMat, GetBoundingRectangle(margin)); + + /// + /// Gets the layer mat with bounding rectangle mat + /// + /// X margin from bounding rectangle + /// Y margin from bounding rectangle + /// + public MatRoi GetLayerMatBoundingRectangle(int marginX, int marginY) => new(LayerMat, GetBoundingRectangle(marginX, marginY)); + + /// + /// Gets the layer mat with bounding rectangle mat + /// + /// Margin from bounding rectangle + /// + public MatRoi GetLayerMatBoundingRectangle(Size margin) => new(LayerMat, GetBoundingRectangle(margin)); + /// /// Gets a new Brg image instance /// @@ -1211,7 +1233,7 @@ public class Layer : BindableBase, IEquatable, IEquatable public string FormatFileNameWithLayerDigits(string prepend = "", IndexStartNumber layerIndexStartNumber = default, string appendExt = ".png") => FormatFileName(prepend, SlicerFile.LayerDigits, layerIndexStartNumber, appendExt); - + public Rectangle GetBoundingRectangle(Mat? mat = null, bool reCalculate = false) { @@ -1252,6 +1274,18 @@ public class Layer : BindableBase, IEquatable, IEquatable return BoundingRectangle; } + public Rectangle GetBoundingRectangle(int marginX, int marginY) + { + var rect = BoundingRectangle; + if (marginX == 0 && marginY == 0) return rect; + rect.Inflate(marginX / 2, marginY / 2); + SlicerFile.SanitizeBoundingRectangle(ref rect); + return rect; + } + + public Rectangle GetBoundingRectangle(int margin) => GetBoundingRectangle(margin, margin); + public Rectangle GetBoundingRectangle(Size margin) => GetBoundingRectangle(margin.Width, margin.Height); + public bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value) { if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.PositionZ)) diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index 2332c13..1144497 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -650,14 +650,15 @@ public abstract class Operation : BindableBase, IDisposable /// Returns a mask given /// /// + /// /// - public Mat? GetMask(Mat mat) => GetMask(_maskPoints, mat); + public Mat? GetMask(Mat mat, Point offset = default) => GetMask(_maskPoints, mat, offset); - public Mat? GetMask(Point[][]? points, Mat mat) + public Mat? GetMask(Point[][]? points, Mat mat, Point offset = default) { if (!HaveMask) return null; - var mask = mat.CreateMask(points!); + var mask = mat.CreateMask(points!, offset); return GetRoiOrDefault(mask); } diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs index 3a2b9fc..56c0a2d 100644 --- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs +++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs @@ -125,72 +125,54 @@ public sealed class OperationLayerExportHeatMap : Operation protected override bool ExecuteInternally(OperationProgress progress) { - using var sumMat32 = EmguExtensions.InitMat(SlicerFile.Resolution, 1, DepthType.Cv32S); - var sumMat32Roi = GetRoiOrDefault(sumMat32); - using var mask = GetMask(sumMat32); + using var resultMat = EmguExtensions.InitMat(SlicerFile.Resolution, 1, DepthType.Cv32S); + using var resultMatRoi = GetRoiOrDefault(resultMat); + using var mask = GetMask(resultMat, HaveROI ? ROI.Location.Invert() : default); var layerRange = _mergeSamePositionedLayers - ? SlicerFile.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd) .ToArray() + ? SlicerFile.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd).ToArray() : GetSelectedLayerRange().ToArray(); progress.ItemCount = (uint)layerRange.Length; - /*Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex => - { - if (progress.Token.IsCancellationRequested) return; - - using var mat = SlicerFile[layerIndex].LayerMat; - using var mat32 = new Mat(); - mat.ConvertTo(mat32, DepthType.Cv32S); - var mat32Roi = GetRoiOrDefault(mat32); - - lock (progress.Mutex) - { - CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask); - progress++; - } - });*/ - - Parallel.ForEach(layerRange, CoreSettings.GetParallelOptions(progress), layer => + Parallel.ForEach(layerRange, CoreSettings.GetParallelOptions(progress), layer => { using var mat = _mergeSamePositionedLayers ? SlicerFile.GetMergedMatForSequentialPositionedLayers(layer.Index) : layer.LayerMat; + using var matRoi = GetRoiOrDefault(mat); - using var mat32 = new Mat(); - mat.ConvertTo(mat32, DepthType.Cv32S); - var mat32Roi = GetRoiOrDefault(mat32); + matRoi.ConvertTo(matRoi, DepthType.Cv32S); lock (progress.Mutex) { - CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask); + CvInvoke.Add(resultMatRoi, matRoi, resultMatRoi, mask); progress++; } }); - using var sumMat = EmguExtensions.InitMat(sumMat32.Size); - sumMat32.ConvertTo(sumMat, DepthType.Cv8U, 1.0 / layerRange.Length); + resultMat.ConvertTo(resultMat, DepthType.Cv8U, 1.0 / layerRange.Length); if (_flipDirection != FlipDirection.None) { - CvInvoke.Flip(sumMat, sumMat, (FlipType)_flipDirection); + CvInvoke.Flip(resultMat, resultMat, (FlipType)_flipDirection); } if (_rotateDirection != RotateDirection.None) { - CvInvoke.Rotate(sumMat, sumMat, (RotateFlags)_rotateDirection); + CvInvoke.Rotate(resultMat, resultMat, (RotateFlags)_rotateDirection); } if (_cropByRoi && HaveROI) { - var sumMatRoi = GetRoiOrDefault(sumMat); + var sumMatRoi = GetRoiOrDefault(resultMat); sumMatRoi.Save(_filePath); } else { - sumMat.Save(_filePath); + resultMat.Save(_filePath); } diff --git a/UVtools.Core/Operations/OperationLayerExportMesh.cs b/UVtools.Core/Operations/OperationLayerExportMesh.cs index 16ac082..ef03c3c 100644 --- a/UVtools.Core/Operations/OperationLayerExportMesh.cs +++ b/UVtools.Core/Operations/OperationLayerExportMesh.cs @@ -197,6 +197,53 @@ public sealed class OperationLayerExportMesh : Operation StripAntiAliasing = _stripAntiAliasing }; + /*const float threshold = 0.5f; + + int x_res = SlicerFile.BoundingRectangle.Width; + int y_res = SlicerFile.BoundingRectangle.Height; + int z_res = (int)LayerRangeCount; + float x_grid_min = -(x_res / 2.0f); + float x_grid_max = x_res / 2.0f; + float y_grid_min = -(y_res / 2.0f); + float y_grid_max = y_res / 2.0f; + float z_grid_min = -(z_res / 2.0f); + float z_grid_max = z_res / 2.0f; + + var triangles = new List(); + float[] xyplane0 = new float[x_res * y_res]; + float[] xyplane1 = new float[x_res * y_res]; + + + for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) + { + using var matRoi = SlicerFile[layerIndex].LayerMatModelBoundingRectangle; + using var mat = new Mat(); + matRoi.RoiMat.ConvertTo(mat, DepthType.Cv32F, 1.0 / 255); + + if (layerIndex == LayerIndexStart) + { + xyplane0 = mat.GetDataSpan().ToArray(); + continue; + } + + xyplane1 = mat.GetDataSpan().ToArray(); + + // Calculate triangles for the xy-planes corresponding to z - 1 and z by marching cubes. + MarchingCubes.TesselateAdjacentXyPlanePair( + xyplane0, xyplane1, + (int)(layerIndex - LayerIndexStart - 1), + triangles, + threshold, // Use threshold as isovalue. + x_grid_min, x_grid_max, x_res, + y_grid_min, y_grid_max, y_res, + z_grid_min, z_grid_max, z_res); + + (xyplane0, xyplane1) = (xyplane1, xyplane0); + + progress++; + } + + return true;*/ /* For the 1st stage, we maintain up to 3 mats, the current layer, the one below us, and the one above us * (below will be null when current layer is 0, above will be null when currentlayer is layercount-1) */ @@ -227,7 +274,26 @@ public sealed class OperationLayerExportMesh : Operation var rootFaces = new Voxelizer.UVFace?[distinctLayers.Length]; var layerFaceCounts = new uint[distinctLayers.Length]; var layerTrees = new KdTree[distinctLayers.Length]; - + + void ExitCleanup() + { + /* dispose of everything */ + for (var x = 0; x < layerTrees.Length; x++) + { + layerTrees[x] = null!; + } + + layerTrees = null; + + for (var x = 0; x < rootFaces.Length; x++) + { + if (rootFaces[x] is not null) rootFaces[x]!.FlatListNext = null; + rootFaces[x] = null!; + } + rootFaces = null; + GC.Collect(); + } + progress.Reset("layers", (uint)distinctLayers.Length); progress.Title = "Stage 1: Generating faces from layers"; //progress.ItemCount = LayerRangeCount; @@ -467,7 +533,7 @@ public sealed class OperationLayerExportMesh : Operation if (progress.Token.IsCancellationRequested) { - Cleanup(); + ExitCleanup(); return false; } @@ -497,7 +563,7 @@ public sealed class OperationLayerExportMesh : Operation if (progress.Token.IsCancellationRequested) { - Cleanup(); + ExitCleanup(); return false; } @@ -509,13 +575,8 @@ public sealed class OperationLayerExportMesh : Operation * Since we don't modify the lists/objects and only connect them via doubly linked list * we can process each layer independant of the others. */ - Parallel.For(0, distinctLayers.Length, i => + Parallel.For(0, distinctLayers.Length, CoreSettings.GetParallelOptions(progress), i => { - if (progress.Token.IsCancellationRequested) - { - return; - } - /* if no faces on this layer... skip.... needed for empty layers */ if (layerTrees[i] is null) return; @@ -569,63 +630,49 @@ public sealed class OperationLayerExportMesh : Operation if (progress.Token.IsCancellationRequested) { - Cleanup(); + ExitCleanup(); return false; } progress.Title = "Stage 4: Writing the file"; progress.ProcessedItems = 0; + var tmpFile = PathExtensions.GetTemporaryFilePathWithExtension("stl", $"UVtools{Id}-"); + using (var mesh = fileExtension.FileFormatType.CreateInstance(tmpFile, FileMode.Create, _meshFileFormat, SlicerFile)) + { + mesh!.BeginWrite(); - using var mesh = fileExtension.FileFormatType.CreateInstance(_filePath, FileMode.Create, _meshFileFormat, SlicerFile); - mesh!.BeginWrite(); - - /* Begin Stage 4, generating triangles and saving to file */ - for (var treeIndex = 0; treeIndex < layerTrees.Length; treeIndex++) { - var tree = layerTrees[treeIndex]; - if (tree is null) continue; - - /* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */ - foreach (var p in tree.Where(p => p.Value.Parent is null)) + /* Begin Stage 4, generating triangles and saving to file */ + for (var treeIndex = 0; treeIndex < layerTrees.Length; treeIndex++) { - /* generate the triangles */ - foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth,distinctLayers[treeIndex].PositionZ)) + var tree = layerTrees[treeIndex]; + if (tree is null) continue; + + /* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */ + foreach (var p in tree.Where(p => p.Value.Parent is null)) { - /* write to file */ - mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal); + /* generate the triangles */ + foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth, + distinctLayers[treeIndex].PositionZ)) + { + /* write to file */ + mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal); + } } - } - /* check for cancellation at every layer, and if so, close the file properly */ - if (progress.Token.IsCancellationRequested) - { - Cleanup(); - return false; - } - - progress++; - } - - void Cleanup() - { - /* dispose of everything */ - for (var x = 0; x < layerTrees.Length; x++) - { - layerTrees[x] = null!; - } - - layerTrees = null; + /* check for cancellation at every layer, and if so, close the file properly */ + if (progress.Token.IsCancellationRequested) + { + ExitCleanup(); + return false; + } - for (var x = 0; x < rootFaces.Length; x++) - { - if (rootFaces[x] is not null) rootFaces[x]!.FlatListNext = null; - rootFaces[x] = null!; + progress++; } - rootFaces = null; - GC.Collect(); + mesh.EndWrite(); } - mesh.EndWrite(); + if (!progress.Token.IsCancellationRequested && File.Exists(tmpFile)) File.Move(tmpFile, _filePath, true); return !progress.Token.IsCancellationRequested; } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index aac6bf6..e7d5120 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.8.1 + 3.8.2 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -47,6 +47,10 @@ 1701;1702;1591 + + + + @@ -64,6 +68,7 @@ True \ + diff --git a/UVtools.Core/Voxel/GridCube.cs b/UVtools.Core/Voxel/GridCube.cs new file mode 100644 index 0000000..7f228ac --- /dev/null +++ b/UVtools.Core/Voxel/GridCube.cs @@ -0,0 +1,17 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System.Numerics; + +namespace UVtools.Core.Voxel; + +public class GridCube +{ + public Vector3[] Vertex { get; } = new Vector3[8]; + public float[] Value { get; } = new float[8]; +} \ No newline at end of file diff --git a/UVtools.Core/Voxel/MarchingCubes.cs b/UVtools.Core/Voxel/MarchingCubes.cs new file mode 100644 index 0000000..4e99b53 --- /dev/null +++ b/UVtools.Core/Voxel/MarchingCubes.cs @@ -0,0 +1,540 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.Intrinsics; +using System.Text; +using System.Threading.Tasks; + +namespace UVtools.Core.Voxel; + +public static class MarchingCubes +{ + public static int[] MC_EdgeTable = + { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, // mirrors here... + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + + + public static int[][] MC_TriTable = + { + new[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + new[] {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + new[] {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + new[] {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + new[] {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + new[] {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + new[] {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + new[] {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + new[] {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + new[] {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + new[] {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + new[] {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + new[] {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + new[] {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + new[] {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + new[] {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + new[] {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + new[] {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + new[] {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + new[] {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + new[] {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + new[] {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + new[] {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + new[] {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + new[] {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + new[] {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + new[] {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + new[] {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + new[] {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + new[] {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + new[] {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + new[] {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + new[] {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + new[] {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + new[] {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + new[] {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + new[] {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + new[] {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + new[] {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + new[] {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + new[] {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + new[] {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + new[] {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + new[] {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + new[] {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + new[] {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + new[] {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + new[] {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + new[] {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + new[] {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + new[] {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + new[] {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + new[] {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + new[] {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + new[] {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + new[] {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + new[] {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + new[] {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + new[] {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + new[] {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + new[] {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + new[] {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + new[] {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + new[] {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + new[] {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + new[] {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + new[] {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + new[] {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + new[] {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + new[] {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + new[] {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + new[] {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + new[] {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + new[] {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + new[] {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + new[] {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + new[] {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + new[] {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + new[] {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + new[] {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + new[] {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + new[] {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + new[] {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + new[] {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + new[] {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + new[] {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + new[] {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + new[] {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + new[] {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + new[] {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + new[] {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + new[] {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + new[] {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + new[] {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + new[] {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + new[] {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + new[] {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + new[] {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + new[] {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + new[] {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + new[] {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + new[] {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + new[] {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + new[] {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + new[] {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + new[] {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + new[] {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + new[] {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + new[] {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + new[] {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + new[] {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + + public static Vector3 VertexInterp(float isovalue, Vector3 p1, Vector3 p2, float valp1, float valp2) + { + // Sort the vertices so that cracks don't mess up the water-tightness of the mesh. + // Note: the cracks don't appear if you use doubles instead of floats. + + if (Vector3.Distance(p2, p1) < 0) + { + // swap + (p2, p1) = (p1, p2); + (valp2, valp1) = (valp1, valp2); + } + + const float epsilon = 1e-10f; + + if (Math.Abs(isovalue - valp1) < epsilon) + return p1; + + if (Math.Abs(isovalue - valp2) < epsilon) + return p2; + + if (Math.Abs(valp1 - valp2) < epsilon) + return p1; + + float mu = (isovalue - valp1) / (valp2 - valp1); + + return p1 + (p2 - p1) * mu; + } + + public static ushort TesselateGridCube(float isovalue, GridCube grid, Vector3[] triangles) + { + ushort cubeIndex = 0; + + if (grid.Value[0] < isovalue) cubeIndex |= 1; + if (grid.Value[1] < isovalue) cubeIndex |= 2; + if (grid.Value[2] < isovalue) cubeIndex |= 4; + if (grid.Value[3] < isovalue) cubeIndex |= 8; + if (grid.Value[4] < isovalue) cubeIndex |= 16; + if (grid.Value[5] < isovalue) cubeIndex |= 32; + if (grid.Value[6] < isovalue) cubeIndex |= 64; + if (grid.Value[7] < isovalue) cubeIndex |= 128; + + if (MC_EdgeTable[cubeIndex] == 0) + return 0; + + var vertList = new Vector3[12]; + + if ((MC_EdgeTable[cubeIndex] & 1) == 0) + vertList[0] = VertexInterp(isovalue, grid.Vertex[0], grid.Vertex[1], grid.Value[0], grid.Value[1]); + + if ((MC_EdgeTable[cubeIndex] & 2) == 0) + vertList[1] = VertexInterp(isovalue, grid.Vertex[1], grid.Vertex[2], grid.Value[1], grid.Value[2]); + + if ((MC_EdgeTable[cubeIndex] & 4) == 0) + vertList[2] = VertexInterp(isovalue, grid.Vertex[2], grid.Vertex[3], grid.Value[2], grid.Value[3]); + + if ((MC_EdgeTable[cubeIndex] & 8) == 0) + vertList[3] = VertexInterp(isovalue, grid.Vertex[3], grid.Vertex[0], grid.Value[3], grid.Value[0]); + + if ((MC_EdgeTable[cubeIndex] & 16) == 0) + vertList[4] = VertexInterp(isovalue, grid.Vertex[4], grid.Vertex[5], grid.Value[4], grid.Value[5]); + + if ((MC_EdgeTable[cubeIndex] & 32) == 0) + vertList[5] = VertexInterp(isovalue, grid.Vertex[5], grid.Vertex[6], grid.Value[5], grid.Value[6]); + + if ((MC_EdgeTable[cubeIndex] & 64) == 0) + vertList[6] = VertexInterp(isovalue, grid.Vertex[6], grid.Vertex[7], grid.Value[6], grid.Value[7]); + + if ((MC_EdgeTable[cubeIndex] & 128) == 0) + vertList[7] = VertexInterp(isovalue, grid.Vertex[7], grid.Vertex[4], grid.Value[7], grid.Value[4]); + + if ((MC_EdgeTable[cubeIndex] & 256) == 0) + vertList[8] = VertexInterp(isovalue, grid.Vertex[0], grid.Vertex[4], grid.Value[0], grid.Value[4]); + + if ((MC_EdgeTable[cubeIndex] & 512) == 0) + vertList[9] = VertexInterp(isovalue, grid.Vertex[1], grid.Vertex[5], grid.Value[1], grid.Value[5]); + + if ((MC_EdgeTable[cubeIndex] & 1024) == 0) + vertList[10] = VertexInterp(isovalue, grid.Vertex[2], grid.Vertex[6], grid.Value[2], grid.Value[6]); + + if ((MC_EdgeTable[cubeIndex] & 2048) == 0) + vertList[11] = VertexInterp(isovalue, grid.Vertex[3], grid.Vertex[7], grid.Value[3], grid.Value[7]); + + ushort ntriang = 0; + + for (ushort i = 0; MC_TriTable[cubeIndex][i] != -1; i += 3) + { + triangles[ntriang].X = vertList[MC_TriTable[cubeIndex][i]].X; + triangles[ntriang].Y = vertList[MC_TriTable[cubeIndex][i + 1]].Y; + triangles[ntriang].Z = vertList[MC_TriTable[cubeIndex][i + 2]].Z; + ntriang++; + } + + return ntriang; + } + + public static void TesselateAdjacentXyPlanePair(float[] xyplane0, float[] xyplane1, + int z, + List triangles, float isovalue, + float x_grid_min, float x_grid_max, int x_res, + float y_grid_min, float y_grid_max, int y_res, + float z_grid_min, float z_grid_max, int z_res) + { + float x_step_size = (x_grid_max - x_grid_min) / (x_res - 1); + float y_step_size = (y_grid_max - y_grid_min) / (y_res - 1); + float z_step_size = (z_grid_max - z_grid_min) / (z_res - 1); + + for (int x = 0; x < x_res - 1; x++) + { + for (int y = 0; y < y_res - 1; y++) + { + var tempCube = new GridCube(); + + int x_offset = 0; + int y_offset = 0; + int z_offset = 0; + + // Setup vertex 0 + x_offset = 0; + y_offset = 0; + z_offset = 0; + tempCube.Vertex[0].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[0].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[0].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[0] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[0] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 1 + x_offset = 1; + y_offset = 0; + z_offset = 0; + tempCube.Vertex[1].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[1].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[1].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[1] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[1] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 2 + x_offset = 1; + y_offset = 0; + z_offset = 1; + tempCube.Vertex[2].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[2].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[2].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (0 == z_offset) + tempCube.Value[2] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[2] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 3 + x_offset = 0; + y_offset = 0; + z_offset = 1; + tempCube.Vertex[3].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[3].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[3].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[3] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[3] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 4 + x_offset = 0; + y_offset = 1; + z_offset = 0; + tempCube.Vertex[4].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[4].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[4].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[4] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[4] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 5 + x_offset = 1; + y_offset = 1; + z_offset = 0; + tempCube.Vertex[5].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[5].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[5].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[5] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[5] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 6 + x_offset = 1; + y_offset = 1; + z_offset = 1; + tempCube.Vertex[6].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[6].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[6].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[6] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[6] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Setup vertex 7 + x_offset = 0; + y_offset = 1; + z_offset = 1; + tempCube.Vertex[7].X = x_grid_min + ((x + x_offset) * x_step_size); + tempCube.Vertex[7].Y = y_grid_min + ((y + y_offset) * y_step_size); + tempCube.Vertex[7].Z = z_grid_min + ((z + z_offset) * z_step_size); + + if (z_offset == 0) + tempCube.Value[7] = xyplane0[(x + x_offset) * y_res + (y + y_offset)]; + else + tempCube.Value[7] = xyplane1[(x + x_offset) * y_res + (y + y_offset)]; + + // Generate triangles from cube. + var tempTriangleArray = new Vector3[5]; + + ushort numberOfTrianglesGenerated = TesselateGridCube(isovalue, tempCube, tempTriangleArray); + + for (ushort i = 0; i < numberOfTrianglesGenerated; i++) + triangles.Add(tempTriangleArray[i]); + } + } + } +} diff --git a/UVtools.Core/Voxel/MarchingCubesTable.cs b/UVtools.Core/Voxel/MarchingCubesTable.cs new file mode 100644 index 0000000..44454ee --- /dev/null +++ b/UVtools.Core/Voxel/MarchingCubesTable.cs @@ -0,0 +1,281 @@ +using System.Collections; +using System.Collections.Generic; + +public static class MarchingCubesTables +{ + public static int[][] edgeConnections = { + new int[] {0,1}, new int[] {1,2}, new int[] {2,3}, new int[] {3,0}, + new int[] {4,5}, new int[] {5,6}, new int[] {6,7}, new int[] {7,4}, + new int[] {0,4}, new int[] {1,5}, new int[] {2,6}, new int[] {3,7} + }; + + public static Vector3[] cubeCorners = new Vector3[] { + new Vector3(0, 0, 1), + new Vector3(1, 0, 1), + new Vector3(1, 0, 0), + new Vector3(0, 0, 0), + new Vector3(0, 1, 1), + new Vector3(1, 1, 1), + new Vector3(1, 1, 0), + new Vector3(0, 1, 0) + }; + + public static int[][] triTable = { + new int[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + new int[] {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + new int[] {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + new int[] {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + new int[] {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + new int[] {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + new int[] {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + new int[] {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + new int[] {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + new int[] {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + new int[] {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + new int[] {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + new int[] {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + new int[] {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + new int[] {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + new int[] {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + new int[] {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + new int[] {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + new int[] {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + new int[] {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + new int[] {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + new int[] {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + new int[] {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + new int[] {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + new int[] {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + new int[] {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + new int[] {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + new int[] {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + new int[] {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + new int[] {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + new int[] {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + new int[] {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + new int[] {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + new int[] {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + new int[] {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + new int[] {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + new int[] {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + new int[] {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + new int[] {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + new int[] {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + new int[] {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + new int[] {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + new int[] {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + new int[] {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + new int[] {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + new int[] {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + new int[] {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + new int[] {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + new int[] {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + new int[] {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + new int[] {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + new int[] {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + new int[] {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + new int[] {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + new int[] {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + new int[] {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + new int[] {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + new int[] {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + new int[] {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + new int[] {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + new int[] {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + new int[] {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + new int[] {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + new int[] {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + new int[] {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + new int[] {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + new int[] {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + new int[] {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + new int[] {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + new int[] {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + new int[] {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + new int[] {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + new int[] {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + new int[] {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + new int[] {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + new int[] {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + new int[] {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + new int[] {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + new int[] {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + new int[] {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + new int[] {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + new int[] {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + new int[] {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + new int[] {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + new int[] {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + new int[] {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + new int[] {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + new int[] {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + new int[] {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + new int[] {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + new int[] {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + new int[] {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + new int[] {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + new int[] {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + new int[] {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + new int[] {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + new int[] {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + new int[] {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + new int[] {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + new int[] {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + new int[] {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + new int[] {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + new int[] {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + new int[] {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + new int[] {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + new int[] {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + new int[] {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + new int[] {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + new int[] {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + new int[] {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + new int[] {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + new int[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; +} diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs index e6fb7c3..e5bd116 100644 --- a/UVtools.WPF/App.axaml.cs +++ b/UVtools.WPF/App.axaml.cs @@ -49,21 +49,6 @@ public class App : Application DefaultDark } - private static bool _isDebug; - - public static bool IsDebug - { - get - { -#if DEBUG - return true; -#else - return _isDebug; -#endif - } - set => _isDebug = value; - } - //public static ThemeSelector ThemeSelector { get; set; } public static MainWindow MainWindow = null!; public static FileFormat? SlicerFile = null; @@ -507,5 +492,6 @@ public class App : Application return attributes.Length == 0 ? string.Empty : ((AssemblyCompanyAttribute)attributes[0]).Company; } } -#endregion + + #endregion } \ No newline at end of file diff --git a/UVtools.WPF/Controls/WindowEx.cs b/UVtools.WPF/Controls/WindowEx.cs index 3925d32..adc67a7 100644 --- a/UVtools.WPF/Controls/WindowEx.cs +++ b/UVtools.WPF/Controls/WindowEx.cs @@ -104,7 +104,11 @@ public class WindowEx : Window, INotifyPropertyChanged, IStyleable public double WindowsWidthMaxSizeRatio { get; set; } = 1; public double WindowsHeightMaxSizeRatio { get; set; } = 1; - public bool IsDebug => App.IsDebug; + public bool IsDebug + { + get => Program.IsDebug; + set => Program.IsDebug = value; + } public UserSettings Settings => UserSettings.Instance; diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 1a8b413..050196e 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -15,12 +15,17 @@ using System.Text; using Avalonia; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Threading; +using Emgu.CV; +using Emgu.CV.CvEnum; using MessageBox.Avalonia.Enums; +using UVtools.Core.FileFormats; using UVtools.Core.Layers; using UVtools.Core.Objects; using UVtools.Core.SystemOS; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; +using static Org.BouncyCastle.Bcpg.Attr.ImageAttrib; using Bitmap = Avalonia.Media.Imaging.Bitmap; using Helpers = UVtools.WPF.Controls.Helpers; @@ -183,13 +188,21 @@ public partial class MainWindow } } - public async void OnClickThumbnailImport() + public async void OnClickThumbnailImportFile(bool replaceAll = false) { - if (_visibleThumbnailIndex <= 0) return; - if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null) + if (replaceAll) { - return; // This should never happen! + if (SlicerFile.ThumbnailsCount == 0) return; + } + else + { + if (_visibleThumbnailIndex <= 0) return; + if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null) + { + return; // This should never happen! + } } + var dialog = new OpenFileDialog { @@ -200,11 +213,144 @@ public partial class MainWindow var filepath = await dialog.ShowAsync(this); if (filepath is null || filepath.Length <= 0) return; - uint i = _visibleThumbnailIndex - 1; - SlicerFile.SetThumbnail((int)i, filepath[0]); - //VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap(); - SlicerFile.RequireFullEncode = true; - CanSave = true; + + bool result; + + if (replaceAll) + { + result = SlicerFile.SetThumbnails(filepath[0]) > 0; + } + else + { + uint i = _visibleThumbnailIndex - 1; + result = SlicerFile.SetThumbnail((int)i, filepath[0]); + } + + if (result) CanSave = true; + } + + public void OnClickThumbnailImportCurrentLayer(bool replaceAll = false) + { + if (!LayerCache.IsCached || SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return; + if (replaceAll) + { + if (SlicerFile.ThumbnailsCount == 0) return; + } + else + { + if (_visibleThumbnailIndex <= 0) return; + if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null) + { + return; // This should never happen! + } + } + + using var matRoi = new Mat(LayerCache.Image, LayerCache.Layer.GetBoundingRectangle(50, 100)); + using var thumbnailMat = new Mat(); + CvInvoke.CvtColor(matRoi, thumbnailMat, ColorConversion.Gray2Bgr); + + bool result; + + if (replaceAll) + { + result = SlicerFile.SetThumbnails(thumbnailMat) > 0; + } + else + { + uint i = _visibleThumbnailIndex - 1; + result = SlicerFile.SetThumbnail((int)i, thumbnailMat); + } + + if (result) CanSave = true; + } + + public void OnClickThumbnailImportRandomLayer(bool replaceAll = false) + { + if (SlicerFile.LayerCount == 0 || SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return; + if (replaceAll) + { + if (SlicerFile.ThumbnailsCount == 0) return; + } + else + { + if (_visibleThumbnailIndex <= 0) return; + if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null) + { + return; // This should never happen! + } + } + + var layer = SlicerFile[Random.Shared.Next((int) SlicerFile.LayerCount)]; + using var matRoi = layer.GetLayerMatBoundingRectangle(50, 100); + CvInvoke.CvtColor(matRoi.RoiMat, matRoi.RoiMat, ColorConversion.Gray2Bgr); + + bool result; + + if (replaceAll) + { + result = SlicerFile.SetThumbnails(matRoi.RoiMat) > 0; + } + else + { + uint i = _visibleThumbnailIndex - 1; + result = SlicerFile.SetThumbnail((int)i, matRoi.RoiMat); + } + + if (result) CanSave = true; + } + + public async void OnClickThumbnailImportHeatmap(bool replaceAll = false) + { + if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return; + if (replaceAll) + { + if (SlicerFile.ThumbnailsCount == 0) return; + } + else + { + if (_visibleThumbnailIndex <= 0) return; + if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null) + { + return; // This should never happen! + } + } + + IsGUIEnabled = false; + ShowProgressWindow("Generating heatmap"); + + Mat? mat = null; + + try + { + mat = await SlicerFile.GenerateHeatmapAsync(SlicerFile.GetBoundingRectangle(100, 50, Progress), Progress); + CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr); + } + catch (OperationCanceledException) + { } + catch (Exception exception) + { + await this.MessageBoxError(exception.ToString(), "Error while generating the heatmap"); + } + + IsGUIEnabled = true; + + if (mat is null) return; + + bool result; + + if (replaceAll) + { + result = SlicerFile.SetThumbnails(mat) > 0; + } + else + { + uint i = _visibleThumbnailIndex - 1; + result = SlicerFile.SetThumbnail((int) i, mat); + } + + mat?.Dispose();; + + if(result) CanSave = true; } public void RefreshThumbnail() diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs index c258ecd..91fe024 100644 --- a/UVtools.WPF/MainWindow.LayerPreview.cs +++ b/UVtools.WPF/MainWindow.LayerPreview.cs @@ -849,10 +849,9 @@ public partial class MainWindow { if (!IsFileLoaded) return; - var sanitizedLayerIndex = Math.Min(_actualLayer, SlicerFile.LastLayerIndex); - if (sanitizedLayerIndex != _actualLayer) + + if (SlicerFile.SanitizeLayerIndex(ref _actualLayer)) { - _actualLayer = sanitizedLayerIndex; InvalidateLayerNavigation(); } diff --git a/UVtools.WPF/MainWindow.Suggestions.cs b/UVtools.WPF/MainWindow.Suggestions.cs index 497faa2..62a85a6 100644 --- a/UVtools.WPF/MainWindow.Suggestions.cs +++ b/UVtools.WPF/MainWindow.Suggestions.cs @@ -15,7 +15,6 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Threading; using MessageBox.Avalonia.Enums; -using UVtools.Core.Managers; using UVtools.Core.Suggestions; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; @@ -53,16 +52,14 @@ public partial class MainWindow { var suggestionsAvailable = new List(); var suggestionsApplied = new List(); + byte autoApplied = 0; foreach (var suggestion in Suggestions) { suggestion.SlicerFile = SlicerFile; if(!suggestion.Enabled || !suggestion.IsAvailable) continue; - if (tryToAutoApply) + if (tryToAutoApply && suggestion.ExecuteIfAutoApply()) { - if (suggestion.ExecuteIfAutoApply()) - { - CanSave = true; - } + autoApplied++; } if(suggestion.IsApplied) suggestionsApplied.Add(suggestion); @@ -71,6 +68,13 @@ public partial class MainWindow SuggestionsAvailable.ReplaceCollection(suggestionsAvailable); SuggestionsApplied.ReplaceCollection(suggestionsApplied); + + if (autoApplied > 0) + { + CanSave = true; + ResetDataContext(); + ForceUpdateActualLayer(); + } } public async void ApplySuggestionsClicked() diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index af73fa0..0363e7a 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -236,6 +236,11 @@ IsVisible="{Binding IsDebug}" i:MenuItem.Icon="fa-solid fa-bug-slash"> + + -