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

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-11-06 02:07:16 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-11-06 02:07:16 +0300
commit0c63caa547a0aa15ac5326158eed232823aff992 (patch)
tree6d0b4e348a66afa52c049eed188b30f8651088a9
parent9bfe534e554ebd1a9f0b3c8195f32cf08ad6d1ec (diff)
v3.8.2v3.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 <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)
-rw-r--r--CHANGELOG.md24
-rw-r--r--README.md18
-rw-r--r--RELEASE_NOTES.md33
-rw-r--r--UVtools.Cmd/Program.cs1
-rw-r--r--UVtools.Cmd/Symbols/ExtractCommand.cs2
-rw-r--r--UVtools.Cmd/Symbols/SetThumbnailCommand.cs148
-rw-r--r--UVtools.Cmd/UVtools.Cmd.csproj2
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs8
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs156
-rw-r--r--UVtools.Core/Gerber/GerberDocument.cs9
-rw-r--r--UVtools.Core/Layers/Layer.cs36
-rw-r--r--UVtools.Core/Operations/Operation.cs7
-rw-r--r--UVtools.Core/Operations/OperationLayerExportHeatMap.cs44
-rw-r--r--UVtools.Core/Operations/OperationLayerExportMesh.cs149
-rw-r--r--UVtools.Core/UVtools.Core.csproj7
-rw-r--r--UVtools.Core/Voxel/GridCube.cs17
-rw-r--r--UVtools.Core/Voxel/MarchingCubes.cs540
-rw-r--r--UVtools.Core/Voxel/MarchingCubesTable.cs281
-rw-r--r--UVtools.WPF/App.axaml.cs18
-rw-r--r--UVtools.WPF/Controls/WindowEx.cs6
-rw-r--r--UVtools.WPF/MainWindow.Information.cs164
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs5
-rw-r--r--UVtools.WPF/MainWindow.Suggestions.cs16
-rw-r--r--UVtools.WPF/MainWindow.axaml72
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs11
-rw-r--r--UVtools.WPF/Program.cs20
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs66
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--documentation/UVtools.Core.xml68
29 files changed, 1751 insertions, 179 deletions
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 <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)
+
## 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 <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
- 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
+ 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
```
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 <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)
+
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. <https://fsf.org/>
+ * 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<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 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 @@
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>UVtoolsCmd</AssemblyName>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
- <Version>1.0.3</Version>
+ <Version>1.0.4</Version>
<Authors>Tiago Conceição, sn4k3</Authors>
<Company>PTRTECH</Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
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<FileFor
ResolutionX = (uint) value.Width;
ResolutionY = (uint) value.Height;
RaisePropertyChanged();
+ RaisePropertyChanged(nameof(ResolutionRectangle));
RaisePropertyChanged(nameof(DisplayAspectRatio));
RaisePropertyChanged(nameof(DisplayAspectRatioStr));
RaisePropertyChanged(nameof(Xppmm));
@@ -1539,6 +1540,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
public abstract uint ResolutionY { get; set; }
/// <summary>
+ /// Gets an rectangle that starts at 0,0 and goes up to <see cref="Resolution"/>
+ /// </summary>
+ public Rectangle ResolutionRectangle => new(Point.Empty, Resolution);
+
+ /// <summary>
/// Gets the display total number of pixels (<see cref="ResolutionX"/> * <see cref="ResolutionY"/>)
/// </summary>
public uint DisplayPixelCount => ResolutionX * ResolutionY;
@@ -3331,10 +3337,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
/// Sets thumbnails from a list of thumbnails and clone them
/// </summary>
/// <param name="images"></param>
- 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<FileFor
{
CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]);
}
+
+ changed++;
}
+ if (changed <= 0) return 0;
+
RaisePropertyChanged(nameof(Thumbnails));
+ RequireFullEncode = true;
+ return changed;
+
}
/// <summary>
/// Sets all thumbnails the same image
/// </summary>
/// <param name="image">Image to set</param>
- public void SetThumbnails(Mat image)
+ public byte SetThumbnails(Mat image)
+ {
+ return SetThumbnails(new[] {image});
+ }
+
+ /// <summary>
+ /// Sets all thumbnails from a disk file
+ /// </summary>
+ /// <param name="filePath"></param>
+ public byte SetThumbnails(string filePath)
+ {
+ if (!File.Exists(filePath)) return 0;
+ using var image = CvInvoke.Imread(filePath, ImreadModes.Color);
+ return SetThumbnails(image);
+ }
+
+ /// <summary>
+ /// Sets a thumbnail from mat
+ /// </summary>
+ /// <param name="index">Thumbnail index</param>
+ /// <param name="image"></param>
+ 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;
}
+
/// <summary>
/// Sets a thumbnail from a disk file
/// </summary>
/// <param name="index">Thumbnail index</param>
/// <param name="filePath"></param>
- 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;
}
/// <summary>
@@ -5200,6 +5237,12 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
public Point DisplayToPixelPosition(float x, float y) => 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<FileFor
return _boundingRectangle;
}
+ public Rectangle GetBoundingRectangle(int marginX, int marginY, OperationProgress? progress = null)
+ {
+ var rect = GetBoundingRectangle(progress);
+ if (marginX == 0 && marginY == 0) return rect;
+ rect.Inflate(marginX / 2, marginY / 2);
+ SanitizeBoundingRectangle(ref rect);
+ return rect;
+ }
+
+ public Rectangle GetBoundingRectangle(int margin, OperationProgress? progress = null) => GetBoundingRectangle(margin, margin, progress);
+ public Rectangle GetBoundingRectangle(Size margin, OperationProgress? progress = null) => GetBoundingRectangle(margin.Width, margin.Height, progress);
+
/// <summary>
/// Creates a empty mat of file <see cref="Resolution"/> size and create a dummy pixel to prevent a empty layer detection
@@ -5629,6 +5684,18 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
#region Layer methods
/// <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>
+ /// <returns>True if sanitized, otherwise false</returns>
+ public bool SanitizeLayerIndex(ref uint layerIndex)
+ {
+ var originalValue = layerIndex;
+ layerIndex = Math.Min(layerIndex, LastLayerIndex);
+ return originalValue != layerIndex;
+ }
+
+ /// <summary>
/// Re-assign layer indexes and parent <see cref="FileFormat"/>
/// </summary>
public void SanitizeLayers()
@@ -6171,4 +6238,65 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
*/
}
#endregion
+
+ #region Generators methods
+
+ /// <summary>
+ /// Generates a heatmap based on a stack of layers
+ /// </summary>
+ /// <param name="layerIndexStart">Layer index to start from</param>
+ /// <param name="layerIndexEnd">Layer index to end on</param>
+ /// <param name="roi">Region of interest</param>
+ /// <param name="progress"></param>
+ /// <returns>Heatmap grayscale Mat</returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Generates a heatmap based on a stack of layers
+ /// </summary>
+ /// <param name="roi">Region of interest</param>
+ /// <param name="progress"></param>
+ /// <returns>Heatmap grayscale Mat</returns>
+ public Mat GenerateHeatmap(Rectangle roi, OperationProgress? progress = null) => GenerateHeatmap(0, uint.MaxValue, roi, progress);
+
+ public Task<Mat> GenerateHeatmapAsync(uint layerIndexStart = 0, uint layerIndexEnd = uint.MaxValue, Rectangle roi = default, OperationProgress? progress = null)
+ => Task.Run(() => GenerateHeatmap(layerIndexStart, layerIndexEnd, roi, progress));
+
+ public Task<Mat> 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
@@ -833,6 +833,28 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
public MatRoi GetLayerMat(Rectangle roi) => new(LayerMat, roi);
/// <summary>
+ /// Gets the layer mat with bounding rectangle mat
+ /// </summary>
+ /// <param name="margin">Margin from bounding rectangle</param>
+ /// <returns></returns>
+ public MatRoi GetLayerMatBoundingRectangle(int margin) => new(LayerMat, GetBoundingRectangle(margin));
+
+ /// <summary>
+ /// Gets the layer mat with bounding rectangle mat
+ /// </summary>
+ /// <param name="marginX">X margin from bounding rectangle</param>
+ /// <param name="marginY">Y margin from bounding rectangle</param>
+ /// <returns></returns>
+ public MatRoi GetLayerMatBoundingRectangle(int marginX, int marginY) => new(LayerMat, GetBoundingRectangle(marginX, marginY));
+
+ /// <summary>
+ /// Gets the layer mat with bounding rectangle mat
+ /// </summary>
+ /// <param name="margin">Margin from bounding rectangle</param>
+ /// <returns></returns>
+ public MatRoi GetLayerMatBoundingRectangle(Size margin) => new(LayerMat, GetBoundingRectangle(margin));
+
+ /// <summary>
/// Gets a new Brg image instance
/// </summary>
[XmlIgnore]
@@ -1211,7 +1233,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
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<Layer>, IEquatable<uint>
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 <see cref="MaskPoints"/>
/// </summary>
/// <param name="mat"></param>
+ /// <param name="offset"></param>
/// <returns></returns>
- 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<Vector3>();
+ 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<float>().ToArray();
+ continue;
+ }
+
+ xyplane1 = mat.GetDataSpan<float>().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<float, Voxelizer.UVFace>[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<MeshFile>(tmpFile, FileMode.Create, _meshFileFormat, SlicerFile))
+ {
+ mesh!.BeginWrite();
- using var mesh = fileExtension.FileFormatType.CreateInstance<MeshFile>(_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 @@
<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.1</Version>
+ <Version>3.8.2</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -48,6 +48,10 @@
</PropertyGroup>
<ItemGroup>
+ <Compile Remove="Voxel\MarchingCubesTable.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
<Content Include="UVtools.ico" />
</ItemGroup>
@@ -64,6 +68,7 @@
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
+ <None Include="Voxel\MarchingCubesTable.cs" />
</ItemGroup>
<ItemGroup>
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. <https://fsf.org/>
+ * 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<Vector3> 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<Suggestion>();
var suggestionsApplied = new List<Suggestion>();
+ 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">
+ <MenuItem Header="Open executable directory"
+ IsVisible="{Binding IsDebug}"
+ Command="{Binding MenuHelpDebugOpenExecutableDirectoryClicked}"
+ i:MenuItem.Icon="fa-solid fa-folder-open"/>
+
<MenuItem Header="Throw exception"
IsVisible="{Binding IsDebug}"
Command="{Binding MenuHelpDebugThrowExceptionClicked}"
@@ -414,12 +419,73 @@
<TextBlock VerticalAlignment="Center"
Text="{Binding VisibleThumbnailResolution}"/>
- <Button IsEnabled="{Binding VisibleThumbnailIndex}"
+ <uc:ButtonWithIcon IsEnabled="{Binding VisibleThumbnailIndex}"
ToolTip.Tip="Replace the current preview image"
- i:Attached.Icon="fa-solid fa-file-image"
- Command="{Binding OnClickThumbnailImport}"/>
+ Spacing="1"
+ Text="⮟"
+ Icon="fa-solid fa-file-image"
+ Command="{Binding OnClickThumbnailImportFile}"
+ CommandParameter="False">
+ <uc:ButtonWithIcon.ContextMenu>
+ <ContextMenu Name="ThumbnailImportContextMenu" PlacementMode="Bottom">
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportFile}"
+ CommandParameter="False"
+ Header="From file"
+ i:MenuItem.Icon="fa-solid fa-file-image"/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportFile}"
+ CommandParameter="True"
+ Header="From file (Replace all)"
+ i:MenuItem.Icon="fa-solid fa-file-image"/>
+
+ <Separator/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportCurrentLayer}"
+ CommandParameter="False"
+ Header="From current layer"
+ i:MenuItem.Icon="fa-solid fa-layer-group"/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportCurrentLayer}"
+ CommandParameter="True"
+ Header="From current layer (Replace all)"
+ i:MenuItem.Icon="fa-solid fa-layer-group"/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportRandomLayer}"
+ CommandParameter="False"
+ Header="From random layer"
+ i:MenuItem.Icon="fa-solid fa-layer-group"/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportRandomLayer}"
+ CommandParameter="True"
+ Header="From random layer (Replace all)"
+ i:MenuItem.Icon="fa-solid fa-layer-group"/>
+
+ <Separator/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportHeatmap}"
+ CommandParameter="False"
+ Header="From heatmap"
+ i:MenuItem.Icon="fa-solid fa-file-image"/>
+
+ <MenuItem
+ Command="{Binding OnClickThumbnailImportHeatmap}"
+ CommandParameter="True"
+ Header="From heatmap (Replace all)"
+ i:MenuItem.Icon="fa-solid fa-file-image"/>
+
+ </ContextMenu>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
<Button IsEnabled="{Binding VisibleThumbnailIndex}"
+ VerticalAlignment="Stretch"
ToolTip.Tip="Save thumbnail image to a file"
i:Attached.Icon="fa-solid fa-floppy-disk"
Command="{Binding OnClickThumbnailSave}"/>
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index f755a47..69a8ec1 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -201,7 +201,7 @@ public partial class MainWindow : WindowEx
_firstTimeOnIssues = false;
if (ReferenceEquals(_selectedTabItem, TabIssues) && Settings.Issues.ComputeIssuesOnClickTab)
{
- OnClickDetectIssues().ConfigureAwait(false);
+ Dispatcher.UIThread.InvokeAsync(async () => await OnClickDetectIssues());
}
}
}
@@ -1173,6 +1173,11 @@ public partial class MainWindow : WindowEx
}
await new PrusaSlicerManagerWindow().ShowDialog(this);
}
+
+ public void MenuHelpDebugOpenExecutableDirectoryClicked()
+ {
+ SystemAware.StartProcess(App.ApplicationPath);
+ }
public void MenuHelpDebugThrowExceptionClicked()
{
@@ -1726,6 +1731,8 @@ public partial class MainWindow : WindowEx
UpdateLayerTrackerHighlightIssues();
};
+ TabGCode.IsVisible = HaveGCode;
+
if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
{
if (Settings.Issues.ComputeIssuesOnLoad)
@@ -1754,8 +1761,6 @@ public partial class MainWindow : WindowEx
}
}
- TabGCode.IsVisible = HaveGCode;
-
SlicerFile.PropertyChanged += SlicerFileOnPropertyChanged;
PopulateSuggestions();
diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs
index e812193..d6dee91 100644
--- a/UVtools.WPF/Program.cs
+++ b/UVtools.WPF/Program.cs
@@ -14,7 +14,23 @@ namespace UVtools.WPF;
public static class Program
{
+ private static bool _isDebug;
+
public static string[] Args = Array.Empty<string>();
+
+ public static bool IsDebug
+ {
+ get
+ {
+#if DEBUG
+ return true;
+#else
+ return _isDebug;
+#endif
+ }
+ set => _isDebug = value;
+ }
+
public static bool IsCrashReport;
public static Stopwatch ProgramStartupTime = null!;
@@ -38,13 +54,13 @@ public static class Program
Console.WriteLine(e);
return;
}
-
+
if (Args.Length >= 1)
{
switch (Args[0])
{
case "--debug":
- App.IsDebug = true;
+ IsDebug = true;
break;
case "--crash-report" when Args.Length >= 3:
IsCrashReport = true;
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 3a12632..67bb7dd 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -13,6 +13,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using UVtools.Core;
@@ -216,10 +217,11 @@ public class AppVersionChecker : BindableBase
else if (downloadFilename.EndsWith(".AppImage") && Linux.IsRunningAppImageGetPath(out var appImagePath)) // Linux AppImage
{
var directory = Path.GetDirectoryName(appImagePath);
+ const string newFilename = $"{About.Software}.AppImage";
//var oldFileName = Path.GetFileName(appImagePath);
// Try to keep same filename logic if user renamed the file, like UVtools.AppImage would keep same same
//var newFilename = Regex.Replace(oldFileName, @"v\d+.\d+.\d+", $"v{_version}");
- var newFullPath = Path.Combine(directory, downloadFilename);
+ var newFullPath = Path.Combine(directory, newFilename);
if (File.Exists(appImagePath)) File.Delete(appImagePath);
File.Move(DownloadedFile, newFullPath, true);
@@ -237,6 +239,7 @@ public class AppVersionChecker : BindableBase
var upgradeScriptFileName = "upgrade.sh";
var upgradeScriptFilePath = Path.Combine(tmpDirectory, upgradeScriptFileName);
+ var applicationPath = App.ApplicationPath;
await using (var stream = File.CreateText(upgradeScriptFilePath))
{
stream.NewLine = "\n";
@@ -245,9 +248,13 @@ public class AppVersionChecker : BindableBase
await stream.WriteLineAsync("testcmd() { command -v \"$1\" &> /dev/null; }");
await stream.WriteLineAsync();
await stream.WriteLineAsync("cd \"$(dirname \"$0\")\"");
- await stream.WriteLineAsync($"echo '{About.Software} v{About.VersionStr} updater script'");
+ await stream.WriteLineAsync($"echo '{About.Software} v{About.VersionStr} -> {_version} updater script'");
+ await stream.WriteLineAsync();
+ await stream.WriteLineAsync($"oldversion='{About.VersionStr}'");
+ await stream.WriteLineAsync($"newversion='{_version}'");
await stream.WriteLineAsync();
//await stream.WriteLineAsync($"cd '{App.ApplicationPath}'");
+ await stream.WriteLineAsync("echo '- Killing processes'");
await stream.WriteLineAsync($"killall {About.Software}");
await stream.WriteLineAsync($"ps -ef | grep '.*dotnet.*{About.Software}.dll' | grep -v grep | awk '{{print $2}}' | xargs kill");
await stream.WriteLineAsync("sleep 1");
@@ -255,9 +262,11 @@ public class AppVersionChecker : BindableBase
if (OperatingSystem.IsMacOS())
{
+ await stream.WriteLineAsync("echo '- Removing com.apple.quarantine flag'");
await stream.WriteLineAsync($"find '{extractDirectoryPath}' -print0 | xargs -0 xattr -d com.apple.quarantine &> /dev/null");
}
+ await stream.WriteLineAsync("echo '- Sync/copying files over'");
if (macOS.IsRunningAppGetPath(out var macOSAppPath) && Directory.Exists(Path.Combine(extractDirectoryPath, "UVtools.app")))
{
await stream.WriteLineAsync("if testcmd rsync; then");
@@ -265,7 +274,6 @@ public class AppVersionChecker : BindableBase
await stream.WriteLineAsync("else");
await stream.WriteLineAsync($" cp -fR '{extractDirectoryPath}/'* '{macOSAppPath}'");
await stream.WriteLineAsync("fi");
- //await stream.WriteLineAsync($"open -n '{macOSAppPath}'");
}
else // Linux generic and macOS generic
{
@@ -274,19 +282,67 @@ public class AppVersionChecker : BindableBase
await stream.WriteLineAsync("else");
await stream.WriteLineAsync($" cp -fR '{extractDirectoryPath}/'* '{App.ApplicationPath}'");
await stream.WriteLineAsync("fi");
+
+ if (About.VersionStr != _version)
+ {
+ var di = new DirectoryInfo(App.ApplicationPath);
+ var newDirectoryName = Regex.Replace(di.Name, $@"({About.Software}.*)(v\d+.\d+.\d+)", $@"$1v{_version}", RegexOptions.IgnoreCase);
+ if (di.Name != newDirectoryName)
+ {
+ await stream.WriteLineAsync("echo '- Directory is able to rename version name'");
+ applicationPath = Path.Combine(di.Parent?.FullName!, newDirectoryName);
+ }
+ }
}
await stream.WriteLineAsync();
- await stream.WriteLineAsync($"nohup bash '{Path.Combine(App.ApplicationPath, "UVtools.sh")}' &> /dev/null &");
+ await stream.WriteLineAsync("sleep 0.5");
+
+ if (App.ApplicationPath == applicationPath)
+ {
+ await stream.WriteLineAsync($"cd '{applicationPath}'");
+ }
+ else // Can move
+ {
+ await stream.WriteLineAsync("echo '- Attempt to rename directory to the new version name'");
+ await stream.WriteLineAsync($"if [ -d '{applicationPath}' ]; then");
+ await stream.WriteLineAsync($" cd '{App.ApplicationPath}'");
+ await stream.WriteLineAsync("else");
+ await stream.WriteLineAsync($" mv -f '{App.ApplicationPath}' '{applicationPath}'");
+ await stream.WriteLineAsync($" cd '{applicationPath}'");
+ await stream.WriteLineAsync("fi");
+ }
+
+ await stream.WriteLineAsync("echo '- UVtools will now launch'");
+ if (App.SlicerFile is not null && App.SlicerFile.FileFullPath != Path.Combine(App.ApplicationPath, About.DemoFile))
+ {
+ // Reload last file
+ await stream.WriteLineAsync($"nohup bash 'UVtools.sh' '{App.SlicerFile.FileFullPath}' &> /dev/null &");
+ }
+ else
+ {
+ await stream.WriteLineAsync("nohup bash 'UVtools.sh' &> /dev/null &");
+ }
await stream.WriteLineAsync("disown");
await stream.WriteLineAsync();
+ await stream.WriteLineAsync($"echo '- Removing: {tmpDirectory}'");
await stream.WriteLineAsync($"rm -fr '{tmpDirectory}'");
+ await stream.WriteLineAsync("echo '- Completed'");
//await stream.WriteLineAsync("sleep 0.5");
//await stream.WriteLineAsync($"rm -f {upgradeScriptFileName}");
//await stream.WriteLine("exit");
}
- SystemAware.StartProcess("bash", $"\"{upgradeScriptFilePath}\"");
+ if (Program.IsDebug)
+ {
+ Console.WriteLine("In debug mode, will not auto-upgrade, please run:");
+ Console.WriteLine($"bash \"{upgradeScriptFilePath}\"");
+ }
+ else
+ {
+ SystemAware.StartProcess("bash", $"\"{upgradeScriptFilePath}\"");
+ }
+
//App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
}
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 7fcd899..27ad74a 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.1</Version>
+ <Version>3.8.2</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml
index 1345158..feb0c97 100644
--- a/documentation/UVtools.Core.xml
+++ b/documentation/UVtools.Core.xml
@@ -2505,6 +2505,11 @@
Gets the image height resolution
</summary>
</member>
+ <member name="P:UVtools.Core.FileFormats.FileFormat.ResolutionRectangle">
+ <summary>
+ Gets an rectangle that starts at 0,0 and goes up to <see cref="P:UVtools.Core.FileFormats.FileFormat.Resolution"/>
+ </summary>
+ </member>
<member name="P:UVtools.Core.FileFormats.FileFormat.DisplayPixelCount">
<summary>
Gets the display total number of pixels (<see cref="P:UVtools.Core.FileFormats.FileFormat.ResolutionX"/> * <see cref="P:UVtools.Core.FileFormats.FileFormat.ResolutionY"/>)
@@ -3103,6 +3108,19 @@
</summary>
<param name="image">Image to set</param>
</member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.SetThumbnails(System.String)">
+ <summary>
+ Sets all thumbnails from a disk file
+ </summary>
+ <param name="filePath"></param>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.SetThumbnail(System.Int32,Emgu.CV.Mat)">
+ <summary>
+ Sets a thumbnail from mat
+ </summary>
+ <param name="index">Thumbnail index</param>
+ <param name="image"></param>
+ </member>
<member name="M:UVtools.Core.FileFormats.FileFormat.SetThumbnail(System.Int32,System.String)">
<summary>
Sets a thumbnail from a disk file
@@ -3618,6 +3636,13 @@
<param name="layerIndex">Layer index to check</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"/>
+ </summary>
+ <param name="layerIndex">Layer index to sanitize</param>
+ <returns>True if sanitized, otherwise false</returns>
+ </member>
<member name="M:UVtools.Core.FileFormats.FileFormat.SanitizeLayers">
<summary>
Re-assign layer indexes and parent <see cref="T:UVtools.Core.FileFormats.FileFormat"/>
@@ -3640,6 +3665,24 @@
<param name="zeroLightOffDelay">If true also set light off to 0, otherwise current value will be kept.</param>
</summary>
</member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GenerateHeatmap(System.UInt32,System.UInt32,System.Drawing.Rectangle,UVtools.Core.Operations.OperationProgress)">
+ <summary>
+ Generates a heatmap based on a stack of layers
+ </summary>
+ <param name="layerIndexStart">Layer index to start from</param>
+ <param name="layerIndexEnd">Layer index to end on</param>
+ <param name="roi">Region of interest</param>
+ <param name="progress"></param>
+ <returns>Heatmap grayscale Mat</returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GenerateHeatmap(System.Drawing.Rectangle,UVtools.Core.Operations.OperationProgress)">
+ <summary>
+ Generates a heatmap based on a stack of layers
+ </summary>
+ <param name="roi">Region of interest</param>
+ <param name="progress"></param>
+ <returns>Heatmap grayscale Mat</returns>
+ </member>
<member name="T:UVtools.Core.FileFormats.FlashForgeSVGXFile.Preview">
<summary>
The files contain two preview images.
@@ -4891,6 +4934,28 @@
<param name="roi">Region of interest</param>
<returns></returns>
</member>
+ <member name="M:UVtools.Core.Layers.Layer.GetLayerMatBoundingRectangle(System.Int32)">
+ <summary>
+ Gets the layer mat with bounding rectangle mat
+ </summary>
+ <param name="margin">Margin from bounding rectangle</param>
+ <returns></returns>
+ </member>
+ <member name="M:UVtools.Core.Layers.Layer.GetLayerMatBoundingRectangle(System.Int32,System.Int32)">
+ <summary>
+ Gets the layer mat with bounding rectangle mat
+ </summary>
+ <param name="marginX">X margin from bounding rectangle</param>
+ <param name="marginY">Y margin from bounding rectangle</param>
+ <returns></returns>
+ </member>
+ <member name="M:UVtools.Core.Layers.Layer.GetLayerMatBoundingRectangle(System.Drawing.Size)">
+ <summary>
+ Gets the layer mat with bounding rectangle mat
+ </summary>
+ <param name="margin">Margin from bounding rectangle</param>
+ <returns></returns>
+ </member>
<member name="P:UVtools.Core.Layers.Layer.BrgMat">
<summary>
Gets a new Brg image instance
@@ -6177,11 +6242,12 @@
</summary>
<param name="points"></param>
</member>
- <member name="M:UVtools.Core.Operations.Operation.GetMask(Emgu.CV.Mat)">
+ <member name="M:UVtools.Core.Operations.Operation.GetMask(Emgu.CV.Mat,System.Drawing.Point)">
<summary>
Returns a mask given <see cref="P:UVtools.Core.Operations.Operation.MaskPoints"/>
</summary>
<param name="mat"></param>
+ <param name="offset"></param>
<returns></returns>
</member>
<member name="M:UVtools.Core.Operations.Operation.ApplyMask(Emgu.CV.Mat,Emgu.CV.Mat,Emgu.CV.Mat)">