diff options
44 files changed, 1014 insertions, 9977 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b3743..effa949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## ? - v0.6 + +* (Add) UVtoolsCmd, a terminal muti-platform aplication to run almost all operations as the GUI +* (Improvement) Huge performance boost in layer reparing and in every mutator +* (Improvement) Layer preview is now faster +* (Improvement) Islands detection is now better and don't skip any pixel, more islands will show or the region will be bigger +* (Improvement) ResinTrap performance +* (Improvement) Better memory optimization by dispose all objects on operations +* (Change) Image engine changed to use only OpenCV Mat instead of two and avoid converting from one to another, as result there's a huge performance gain in some operations +* (Change) UVtools now rely on UVtools.Core, and drop the UVtools.Parser. The Core now perform all operations and transformations inplace of the GUI +* (Change) Islands search are now faster, it will jump from island to insland instead of search in every pixel by pixel +* (Removed) ImageSharp dependency +* (Removed) UVtools.Parser project +* (Fix) Fade resizes make image offset a pixel from layer to layer because of integer placement, now it matain the correct position + ## 21/06/2020 - v0.5.2.2 * (Fix) phz: Files with encryption or sliced by chitubox produced black images after save due not setting the image address nor size (Spotted by Burak Cezairli) diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs new file mode 100644 index 0000000..1b8b7cc --- /dev/null +++ b/UVtools.Cmd/Program.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using UVtools.Core; + +namespace UVtools.Cmd +{ + class Program + { + public static async Task<int> Main(params string[] args) + { + Stopwatch sw = new Stopwatch(); + uint count = 0; + var rootCommand = new RootCommand("MSLA/DLP, file analysis, repair, conversion and manipulation") + { + new Option(new []{"-f", "--file"}, "Input file to read") + { + Required = true, + Argument = new Argument<FileSystemInfo>("filepath").ExistingOnly() + }, + new Option(new []{"-o", "--output"}, "Output file to save the modifications, if aware, it saves to the same input file") + { + Argument = new Argument<FileSystemInfo>("filepath") + }, + new Option(new []{"-c", "--convert"}, "Converts input into a output file format by it extension") + { + Argument = new Argument<FileSystemInfo>("filepath") + }, + + new Option(new []{"-p", "--properties"}, "Print a list of all properties/settings"), + new Option(new []{"-i", "--issues"}, "Compute and print a list of all issues"), + new Option(new []{"-r", "--repair"}, "Attempt to repair all issues"){ + Argument = new Argument<int[]>("[start layer index] [end layer index]") + }, + + new Option(new []{"-mr", "--mut-resize"}, "Resizes layer images in a X and/or Y factor, starting from 100% value") + { + Argument = new Argument<int[]>("[x%] [y%] [start layer index] [end layer index] [fade 0/1]") + }, + new Option(new []{"-ms", "--mut-solidify"}, "Closes all inner holes") + { + Argument = new Argument<int[]>("[start layer index] [end layer index]") + }, + new Option(new []{"-me", "--mut-erode"}, "Erodes away the boundaries of foreground object") + { + Argument = new Argument<int[]>("[start iterations] [end iterations] [start layer index] [end layer index] [fade 0/1]") + }, + new Option(new []{"-md", "--mut-dilate"}, "It is just opposite of erosion") + { + Argument = new Argument<int[]>("[start iterations] [end iterations] [start layer index] [end layer index] [fade 0/1]") + }, + new Option(new []{"-mc", "--mut-close"}, "Dilation followed by Erosion") + { + Argument = new Argument<int[]>("[start iterations] [end iterations] [start layer index] [end layer index] [fade 0/1]") + }, + new Option(new []{"-mo", "--mut-open"}, "Erosion followed by Dilation") + { + Argument = new Argument<int[]>("[start iterations] [end iterations] [start layer index] [end layer index] [fade 0/1]") + }, + new Option(new []{"-mg", "--mut-gradient"}, "The difference between dilation and erosion of an image") + { + Argument = new Argument<int[]>("[kernel size] [start layer index] [end layer index] [fade 0/1]") + }, + + new Option(new []{"-mpyr", "--mut-pyr"}, "Performs downsampling step of Gaussian pyramid decomposition") + { + Argument = new Argument<int[]>("[start layer index] [end layer index]") + }, + new Option(new []{"-mgb", "--mut-gaussian-blur"}, "Each pixel is a sum of fractions of each pixel in its neighborhood") + { + Argument = new Argument<int[]>("[aperture] [sigmaX] [sigmaY]") + }, + new Option(new []{"-mmb", "--mut-median-blur"}, "Each pixel becomes the median of its surrounding pixels") + { + Argument = new Argument<ushort>("[aperture]") + }, + + + + + /*new Option(new []{"-ls", "--layer-start"}, "Specify a start layer index to use with some operations as a range") + { + Argument = new Argument<uint>("Layer index") + }, + + new Option(new []{"-le", "--layer-end"}, "Specify a end layer index to use with some operations as a range") + { + Argument = new Argument<uint>("Layer index") + }, + + new Option(new []{"-is", "--iteration-start"}, "Specify a start layer index to use with some operations as a range") + { + Argument = new Argument<uint>("Layer index") + }, + + new Option(new []{"-fade"}, "Fade a start value towards a end value to use with some operations")*/ + + }; + + rootCommand.Handler = CommandHandler.Create( + (FileSystemInfo file, FileSystemInfo convert, bool properties, bool issues, bool repair, uint layerStartIndex, uint layerEndIndex) => + { + Console.WriteLine($"Reading: {file}"); + + var fileFormat = FileFormat.FindByExtension(file.FullName, true, true); + if (ReferenceEquals(fileFormat, null)) + { + Console.WriteLine($"Error: {file.FullName} is not a known nor valid format."); + } + else + { + fileFormat.Decode(file.FullName); + } + + if (properties) + { + count = 0; + Console.WriteLine("Listing all properties:"); + Console.WriteLine("----------------------"); + foreach (var config in fileFormat.Configs) + { + Console.WriteLine("******************************"); + Console.WriteLine($"\t{config.GetType().Name}"); + Console.WriteLine("******************************"); + foreach (PropertyInfo propertyInfo in config.GetType() + .GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + count++; + if (propertyInfo.Name.Equals("Item")) continue; + Console.WriteLine($"{propertyInfo.Name}: {propertyInfo.GetValue(config)}"); + } + } + + Console.WriteLine("----------------------"); + Console.WriteLine($"Total properties: {count}"); + } + + if (issues) + { + Console.WriteLine("Computing Issues, please wait."); + sw.Restart(); + var issuesDict = fileFormat.LayerManager.GetAllIssues(); + sw.Stop(); + + Console.WriteLine("Issues:"); + Console.WriteLine("----------------------"); + count = 0; + for (uint layerIndex = 0; layerIndex < fileFormat.LayerCount; layerIndex++) + { + if(!issuesDict.TryGetValue(layerIndex, out var list)) continue; + foreach (var issue in list) + { + Console.WriteLine(issue); + count++; + } + } + + Console.WriteLine("----------------------"); + Console.WriteLine($"Total Issues: {count} in {sw.ElapsedMilliseconds}ms"); + } + + if (!ReferenceEquals(convert, null)) + { + var fileConvert = FileFormat.FindByExtension(convert.FullName, true, true); + if (ReferenceEquals(fileFormat, null)) + { + Console.WriteLine($"Error: {convert.FullName} is not a known nor valid format."); + } + else + { + Console.WriteLine($"Converting {fileFormat.GetType().Name} to {fileConvert.GetType().Name}: {convert.Name}"); + + try + { + sw.Restart(); + fileFormat.Convert(fileConvert, convert.FullName); + sw.Stop(); + Console.WriteLine($"Convertion done in {sw.ElapsedMilliseconds}ms"); + } + catch (Exception e) + { + Console.WriteLine(e); + } + + } + + } + + + }); + + + await rootCommand.InvokeAsync(args); + //await rootCommand.InvokeAsync("-f body_Tough0.1mm_SL1_5h16m_HOLLOW_DRAIN.sl1 -i"); + + return 1; + + } + } +} diff --git a/UVtools.Cmd/UVtools.Cmd.csproj b/UVtools.Cmd/UVtools.Cmd.csproj new file mode 100644 index 0000000..a944096 --- /dev/null +++ b/UVtools.Cmd/UVtools.Cmd.csproj @@ -0,0 +1,40 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>netcoreapp3.1</TargetFramework> + <AssemblyName>UVtoolsCmd</AssemblyName> + <ApplicationIcon>UVtools.ico</ApplicationIcon> + <Version>0.1</Version> + <Authors>Tiago Conceição</Authors> + <Company>PTRTECH</Company> + <PackageLicenseFile>LICENSE</PackageLicenseFile> + <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> + <PackageIcon>UVtools.png</PackageIcon> + <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> + <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> + <Copyright>Copyright © 2020 PTRTECH</Copyright> + <RepositoryType>Git</RepositoryType> + <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20303.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\UVtools.Core\UVtools.Core.csproj" /> + </ItemGroup> + + <ItemGroup> + <None Include="..\LICENSE"> + <Pack>True</Pack> + <PackagePath></PackagePath> + </None> + <None Include="..\UVtools.GUI\UVtools.png"> + <Pack>True</Pack> + <PackagePath></PackagePath> + </None> + </ItemGroup> + +</Project> diff --git a/UVtools.Cmd/UVtools.ico b/UVtools.Cmd/UVtools.ico Binary files differnew file mode 100644 index 0000000..2a642ec --- /dev/null +++ b/UVtools.Cmd/UVtools.ico diff --git a/UVtools.Console/Program.cs b/UVtools.Console/Program.cs deleted file mode 100644 index 90ccdae..0000000 --- a/UVtools.Console/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using UVtools.Core; - -namespace UVtools.Terminal -{ - class Program - { - static void Main(string[] args) - { - - Console.WriteLine("Hello World!"); - /*FileFormat fs = new SL1File(@"D:\Tiago\Desktop\UVtools\18650_holder_4pcs.sl1"); - fs.Extract(@"D:\Tiago\Desktop\UVtools\18650_holder"); - - FileFormat fs1 = new SL1File(@"D:\Tiago\Desktop\UVtools\coronanew11.ctb"); - fs1.Extract(@"D:\Tiago\Desktop\UVtools\coronanew11");*/ - } - } -} diff --git a/UVtools.Console/UVtools.Terminal.csproj b/UVtools.Console/UVtools.Terminal.csproj deleted file mode 100644 index 07643ef..0000000 --- a/UVtools.Console/UVtools.Terminal.csproj +++ /dev/null @@ -1,13 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <OutputType>Exe</OutputType> - <TargetFramework>netcoreapp3.1</TargetFramework> - <AssemblyName>UVtoolsCmd</AssemblyName> - </PropertyGroup> - - <ItemGroup> - <ProjectReference Include="..\UVtools.Core\UVtools.Core.csproj" /> - </ItemGroup> - -</Project> diff --git a/UVtools.Core/CWSFile.cs b/UVtools.Core/CWSFile.cs index 1743a9e..14e948d 100644 --- a/UVtools.Core/CWSFile.cs +++ b/UVtools.Core/CWSFile.cs @@ -14,7 +14,6 @@ using System.Linq; using System.Reflection; using System.Text; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/ChituboxFile.cs b/UVtools.Core/ChituboxFile.cs index 05f5397..0aed6ce 100644 --- a/UVtools.Core/ChituboxFile.cs +++ b/UVtools.Core/ChituboxFile.cs @@ -19,7 +19,6 @@ using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/ChituboxZipFile.cs b/UVtools.Core/ChituboxZipFile.cs index ec57188..9507b77 100644 --- a/UVtools.Core/ChituboxZipFile.cs +++ b/UVtools.Core/ChituboxZipFile.cs @@ -18,7 +18,6 @@ using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Util; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/FileExtension.cs b/UVtools.Core/FileExtension.cs index cb56563..7838211 100644 --- a/UVtools.Core/FileExtension.cs +++ b/UVtools.Core/FileExtension.cs @@ -5,9 +5,10 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ + using System.Collections.Generic; -namespace UVtools.Parser +namespace UVtools.Core { /// <summary> /// Represents a file extension for slicer file formats diff --git a/UVtools.Core/FileFormat.cs b/UVtools.Core/FileFormat.cs index 812e7ae..110cfac 100644 --- a/UVtools.Core/FileFormat.cs +++ b/UVtools.Core/FileFormat.cs @@ -16,7 +16,6 @@ using System.Text; using System.Threading.Tasks; using Emgu.CV; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { @@ -638,51 +637,6 @@ namespace UVtools.Core return Convert(to.GetType(), fileFullPath); } - public void Resize(uint startLayerIndex, uint endLayerIndex, double x, double y, bool fade) - { - if (x == 1.0 && y == 1.0) return; - - Parallel.For(startLayerIndex, endLayerIndex+1, /*new ParallelOptions { MaxDegreeOfParallelism = 1 },*/ layerIndex => - { - var newX = x; - var newY = y; - if (fade) - { - if (newX != 1.0) - { - double steps = Math.Abs(newX - 1.0) / (endLayerIndex - startLayerIndex); - //maxIteration = Math.Max(iterationsStart, iterationsEnd); - - newX = (float) (newX < 1.0 - ? newX + (layerIndex - startLayerIndex) * steps - : newX - (layerIndex - startLayerIndex) * steps); - - // constrain - //iterations = Math.Min(Math.Max(1, iterations), maxIteration); - } - - if (y != 1.0) - { - double steps = Math.Abs(newY - 1.0) / (endLayerIndex - startLayerIndex); - //maxIteration = Math.Max(iterationsStart, iterationsEnd); - - newY = (float) (newY < 1.0 - ? newY + (layerIndex - startLayerIndex) * steps - : newY - (layerIndex - startLayerIndex) * steps); - - // constrain - //iterations = Math.Min(Math.Max(1, iterations), maxIteration); - } - } - - if (newX == 1.0 && newY == 1.0) return; - - - - this[layerIndex].Resize(newX, newY); - }); - } - public byte ValidateAntiAliasingLevel() { if (AntiAliasing < 2) return 1; diff --git a/UVtools.Core/IFileFormat.cs b/UVtools.Core/IFileFormat.cs index 89c5a09..825804b 100644 --- a/UVtools.Core/IFileFormat.cs +++ b/UVtools.Core/IFileFormat.cs @@ -9,7 +9,6 @@ using System; using System.Text; using Emgu.CV; -using UVtools.Parser; namespace UVtools.Core { @@ -338,16 +337,6 @@ namespace UVtools.Core bool Convert(FileFormat to, string fileFullPath); /// <summary> - /// Resizes layer images in x and y factor, starting at 1 = 100% - /// </summary> - /// <param name="startLayerIndex">Layer index to start</param> - /// <param name="endLayerIndex">Layer index to end</param> - /// <param name="x">X factor, starts at 1</param> - /// <param name="y">Y factor, starts at 1</param> - /// <param name="fade">Fade X/Y towards 100%</param> - void Resize(uint startLayerIndex, uint endLayerIndex, double x, double y, bool fade); - - /// <summary> /// Validate AntiAlias Level /// </summary> byte ValidateAntiAliasingLevel(); diff --git a/UVtools.Core/ImageFile.cs b/UVtools.Core/ImageFile.cs index 462aa75..4d351b5 100644 --- a/UVtools.Core/ImageFile.cs +++ b/UVtools.Core/ImageFile.cs @@ -4,7 +4,6 @@ using System.Net.Mime; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; -using UVtools.Parser; using Size = System.Drawing.Size; namespace UVtools.Core diff --git a/UVtools.Core/LayerManager.cs b/UVtools.Core/LayerManager.cs index b8b2d6b..12792fa 100644 --- a/UVtools.Core/LayerManager.cs +++ b/UVtools.Core/LayerManager.cs @@ -5,6 +5,7 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ + using System; using System.Collections; using System.Collections.Concurrent; @@ -12,8 +13,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Linq; using System.Threading.Tasks; using Emgu.CV; using Emgu.CV.CvEnum; @@ -21,7 +21,7 @@ using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core.Extensions; -namespace UVtools.Parser +namespace UVtools.Core { #region LayerIssue Class @@ -115,7 +115,7 @@ namespace UVtools.Parser public override string ToString() { - return $"{nameof(Type)}: {Type}"; + return $"{nameof(Type)}: {Type}, Layer: {Layer.Index}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Size)}: {Size}"; } } #endregion @@ -565,13 +565,8 @@ namespace UVtools.Parser return result; } - public Layer Clone() - { - return new Layer(Index, CompressedBytes, Filename, ParentLayerManager); - } - #endregion - public void Resize(double xScale, double yScale) + public void MutateResize(double xScale, double yScale) { using (var mat = LayerMat) { @@ -579,6 +574,144 @@ namespace UVtools.Parser LayerMat = mat; } } + + public void MutateSolidify() + { + using (Mat mat = LayerMat) + { + using (Mat filteredMat = new Mat()) + { + CvInvoke.Threshold(mat, filteredMat, 254, 255, ThresholdType.Binary); // Clean AA + + using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) + { + using (Mat hierarchy = new Mat()) + { + CvInvoke.FindContours(filteredMat, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); + var arr = hierarchy.GetData(); + for (int i = 0; i < contours.Size; i++) + { + if ((int) arr.GetValue(0, i, 2) != -1 || (int) arr.GetValue(0, i, 3) == -1) continue; + CvInvoke.DrawContours(mat, contours, i, new MCvScalar(255), -1); + } + } + } + } + + LayerMat = mat; + } + } + + public void MutateErode(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + if(anchor.IsEmpty) anchor = new Point(-1, -1); + if (ReferenceEquals(kernel, null)) + { + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + } + using (Mat dst = LayerMat) + { + CvInvoke.Erode(dst, dst, kernel, anchor, iterations, borderType, borderValue); + LayerMat = dst; + } + } + + public void MutateDilate(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + if (anchor.IsEmpty) anchor = new Point(-1, -1); + if (ReferenceEquals(kernel, null)) + { + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + } + using (Mat dst = LayerMat) + { + CvInvoke.Dilate(dst, dst, kernel, anchor, iterations, borderType, borderValue); + LayerMat = dst; + } + } + + public void MutateOpen(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + if (anchor.IsEmpty) anchor = new Point(-1, -1); + if (ReferenceEquals(kernel, null)) + { + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + } + using (Mat dst = LayerMat) + { + CvInvoke.MorphologyEx(dst, dst, MorphOp.Open, kernel, anchor, iterations, borderType, borderValue); + LayerMat = dst; + } + } + + public void MutateClose(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + if (anchor.IsEmpty) anchor = new Point(-1, -1); + if (ReferenceEquals(kernel, null)) + { + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); + } + using (Mat dst = LayerMat) + { + CvInvoke.MorphologyEx(dst, dst, MorphOp.Close, kernel, anchor, iterations, borderType, borderValue); + LayerMat = dst; + } + } + + public void MutateGradient(int iterations = 1, IInputArray kernel = null, Point anchor = default, BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + if (anchor.IsEmpty) anchor = new Point(-1, -1); + if (ReferenceEquals(kernel, null)) + { + kernel = CvInvoke.GetStructuringElement(ElementShape.Cross, new Size(3, 3), anchor); + } + using (Mat dst = LayerMat) + { + CvInvoke.MorphologyEx(dst, dst, MorphOp.Gradient, kernel, anchor, iterations, borderType, borderValue); + LayerMat = dst; + } + } + + public void MutatePyrDownUp(BorderType borderType = BorderType.Reflect101) + { + using (Mat dst = LayerMat) + { + CvInvoke.PyrDown(dst, dst, borderType); + CvInvoke.PyrUp(dst, dst, borderType); + LayerMat = dst; + } + } + + public void MutateMedianBlur(int aperture = 1) + { + using (Mat dst = LayerMat) + { + CvInvoke.MedianBlur(dst, dst, aperture); + LayerMat = dst; + } + } + + public void MutateGaussianBlur(Size size = default, int sigmaX = 0, int sigmaY = 0, BorderType borderType = BorderType.Reflect101) + { + if(size.IsEmpty) size = new Size(5, 5); + + using (Mat dst = LayerMat) + { + CvInvoke.GaussianBlur(dst, dst, size, sigmaX, sigmaY, borderType); + LayerMat = dst; + } + } + + + + public Layer Clone() + { + return new Layer(Index, CompressedBytes, Filename, ParentLayerManager); + } + + + + #endregion } #endregion @@ -728,6 +861,219 @@ namespace UVtools.Parser return Layers[index]; } + /// <summary> + /// Resizes layer images in x and y factor, starting at 1 = 100% + /// </summary> + /// <param name="startLayerIndex">Layer index to start</param> + /// <param name="endLayerIndex">Layer index to end</param> + /// <param name="x">X factor, starts at 1</param> + /// <param name="y">Y factor, starts at 1</param> + /// <param name="isFade">Fade X/Y towards 100%</param> + public void MutateResize(uint startLayerIndex, uint endLayerIndex, double x, double y, bool isFade) + { + if (x == 1.0 && y == 1.0) return; + + double xSteps = Math.Abs(x - 1.0) / (endLayerIndex - startLayerIndex); + double ySteps = Math.Abs(y - 1.0) / (endLayerIndex - startLayerIndex); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + var newX = x; + var newY = y; + if (isFade) + { + if (newX != 1.0) + { + + //maxIteration = Math.Max(iterationsStart, iterationsEnd); + + newX = (float)(newX < 1.0 + ? newX + (layerIndex - startLayerIndex) * xSteps + : newX - (layerIndex - startLayerIndex) * xSteps); + + // constrain + //iterations = Math.Min(Math.Max(1, iterations), maxIteration); + } + + if (y != 1.0) + { + + //maxIteration = Math.Max(iterationsStart, iterationsEnd); + + newY = (float)(newY < 1.0 + ? newY + (layerIndex - startLayerIndex) * ySteps + : newY - (layerIndex - startLayerIndex) * ySteps); + + // constrain + //iterations = Math.Min(Math.Max(1, iterations), maxIteration); + } + } + + if (newX == 1.0 && newY == 1.0) return; + + this[layerIndex].MutateResize(newX, newY); + }); + } + + public void MutateSolidify(uint startLayerIndex, uint endLayerIndex) + { + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + this[layerIndex].MutateSolidify(); + }); + } + + private void MutateGetVarsIterationFade(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out int iterationSteps, out int maxIteration) + { + iterationSteps = 0; + maxIteration = 0; + isFade = isFade && startLayerIndex != endLayerIndex && iterationsStart != iterationsEnd; + if (!isFade) return; + iterationSteps = (int)Math.Abs((double)(iterationsStart - iterationsEnd) / (endLayerIndex - startLayerIndex)); + maxIteration = Math.Max(iterationsStart, iterationsEnd); + } + + private int MutateGetIterationVar(bool isFade, int iterationsStart, int iterationsEnd, int iterationSteps, int maxIteration, uint startLayerIndex, uint layerIndex) + { + if(!isFade) return iterationsStart; + // calculate iterations based on range + int iterations = (int)(iterationsStart < iterationsEnd + ? iterationsStart + (layerIndex - startLayerIndex) * iterationSteps + : iterationsStart - (layerIndex - startLayerIndex) * iterationSteps); + + // constrain + return Math.Min(Math.Max(1, iterations), maxIteration); + } + + public void MutateErode(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, + IInputArray kernel = null, Point anchor = default, + BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + MutateGetVarsIterationFade( + startLayerIndex, + endLayerIndex, + iterationsStart, + iterationsEnd, + ref isFade, + out var iterationSteps, + out var maxIteration + ); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint) layerIndex); + this[layerIndex].MutateErode(iterations, kernel, anchor, borderType, borderValue); + }); + } + + public void MutateDilate(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, + IInputArray kernel = null, Point anchor = default, + BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + MutateGetVarsIterationFade( + startLayerIndex, + endLayerIndex, + iterationsStart, + iterationsEnd, + ref isFade, + out var iterationSteps, + out var maxIteration + ); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex); + this[layerIndex].MutateDilate(iterations, kernel, anchor, borderType, borderValue); + }); + } + + public void MutateOpen(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, + IInputArray kernel = null, Point anchor = default, + BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + MutateGetVarsIterationFade( + startLayerIndex, + endLayerIndex, + iterationsStart, + iterationsEnd, + ref isFade, + out var iterationSteps, + out var maxIteration + ); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex); + this[layerIndex].MutateOpen(iterations, kernel, anchor, borderType, borderValue); + }); + } + + public void MutateClose(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, + IInputArray kernel = null, Point anchor = default, + BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + MutateGetVarsIterationFade( + startLayerIndex, + endLayerIndex, + iterationsStart, + iterationsEnd, + ref isFade, + out var iterationSteps, + out var maxIteration + ); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex); + this[layerIndex].MutateClose(iterations, kernel, anchor, borderType, borderValue); + }); + } + + public void MutateGradient(uint startLayerIndex, uint endLayerIndex, int iterationsStart = 1, int iterationsEnd = 1, bool isFade = false, + IInputArray kernel = null, Point anchor = default, + BorderType borderType = BorderType.Default, MCvScalar borderValue = default) + { + MutateGetVarsIterationFade( + startLayerIndex, + endLayerIndex, + iterationsStart, + iterationsEnd, + ref isFade, + out var iterationSteps, + out var maxIteration + ); + + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + int iterations = MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, (uint)layerIndex); + this[layerIndex].MutateGradient(iterations, kernel, anchor, borderType, borderValue); + }); + } + + public void MutatePyrDownUp(uint startLayerIndex, uint endLayerIndex, BorderType borderType = BorderType.Default) + { + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + this[layerIndex].MutatePyrDownUp(borderType); + }); + } + + public void MutateMedianBlur(uint startLayerIndex, uint endLayerIndex, int aperture = 1) + { + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + this[layerIndex].MutateMedianBlur(aperture); + }); + } + + public void MutateGaussianBlur(uint startLayerIndex, uint endLayerIndex, Size size = default, int sigmaX = 0, int sigmaY = 0, BorderType borderType = BorderType.Reflect101) + { + Parallel.For(startLayerIndex, endLayerIndex + 1, layerIndex => + { + this[layerIndex].MutateGaussianBlur(size, sigmaX, sigmaY, borderType); + }); + } + public ConcurrentDictionary<uint, List<LayerIssue>> GetAllIssues() { const byte requiredPixelsToSupportIsland = 10; @@ -749,296 +1095,370 @@ namespace UVtools.Parser } });*/ - var layerHollowAreas = new ConcurrentDictionary<uint, List<LayerHollowArea>>(); - - // Detect contours - Parallel.ForEach(this, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layer => + var task1 = new TaskFactory().StartNew(() => { - using (var image = layer.LayerMat) - { - int step = image.Step; - CvInvoke.Threshold(image, image, 1, 255, ThresholdType.Binary); - - var span = image.GetPixelSpan<byte>(); - - // TouchingBounds Checker - List<Point> pixels = new List<Point>(); - for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds + // Detect contours + Parallel.ForEach(this, + //new ParallelOptions{MaxDegreeOfParallelism = 1}, + layer => { - if (span[x] >= minTouchingBondsPixelColor) // Top + using (var image = layer.LayerMat) { - pixels.Add(new Point(x, 0)); - } + int step = image.Step; + var span = image.GetPixelSpan<byte>(); - if (span[step * image.Height - step + x] >= minTouchingBondsPixelColor) // Bottom - { - pixels.Add(new Point(x, image.Height - 1)); - } - } - - for (int y = 0; y < image.Height; y++) // Check Left and Right bounds - { - if (span[y * step] >= minTouchingBondsPixelColor) // Left - { - pixels.Add(new Point(0, y)); - } - - if (span[y * step + step - 1] >= minTouchingBondsPixelColor) // Right - { - pixels.Add(new Point(step - 1, y)); - } - } - - if (pixels.Count > 0) - { - result.TryAdd(layer.Index, new List<LayerIssue> - { - new LayerIssue(layer, LayerIssue.IssueType.TouchingBound, pixels.ToArray()) - }); - } + // TouchingBounds Checker + List<Point> pixels = new List<Point>(); + for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds + { + if (span[x] >= minTouchingBondsPixelColor) // Top + { + pixels.Add(new Point(x, 0)); + } + if (span[step * image.Height - step + x] >= minTouchingBondsPixelColor) // Bottom + { + pixels.Add(new Point(x, image.Height - 1)); + } + } + for (int y = 0; y < image.Height; y++) // Check Left and Right bounds + { + if (span[y * step] >= minTouchingBondsPixelColor) // Left + { + pixels.Add(new Point(0, y)); + } + if (span[y * step + step - 1] >= minTouchingBondsPixelColor) // Right + { + pixels.Add(new Point(step - 1, y)); + } + } + if (pixels.Count > 0) + { + result.TryAdd(layer.Index, new List<LayerIssue> + { + new LayerIssue(layer, LayerIssue.IssueType.TouchingBound, pixels.ToArray()) + }); + } + if (layer.Index == 0) return; // No inslands for layer 0 - var listHollowArea = new List<LayerHollowArea>(); + CvInvoke.Threshold(image, image, 1, 255, ThresholdType.Binary); + VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); + Mat hierarchy = new Mat(); - VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); - Mat hierarchy = new Mat(); + CvInvoke.FindContours(image, contours, hierarchy, RetrType.Ccomp, + ChainApproxMethod.ChainApproxSimple); - CvInvoke.FindContours(image, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); + var arr = hierarchy.GetData(); + // + //hierarchy[i][0]: the index of the next contour of the same level + //hierarchy[i][1]: the index of the previous contour of the same level + //hierarchy[i][2]: the index of the first child + //hierarchy[i][3]: the index of the parent + // - var arr = hierarchy.GetData(); - // - //hierarchy[i][0]: the index of the next contour of the same level - //hierarchy[i][1]: the index of the previous contour of the same level - //hierarchy[i][2]: the index of the first child - //hierarchy[i][3]: the index of the parent - // - - Mat previousImage = null; - Span<byte> previousSpan = null; - Span<byte> imageSpan = null; - for (int i = 0; i < contours.Size; i++) - { - if ((int) arr.GetValue(0, i, 2) != -1 || (int) arr.GetValue(0, i, 3) == -1) // Island - { - if(layer.Index == 0) continue; - if (ReferenceEquals(previousImage, null)) + Mat previousImage = null; + Span<byte> previousSpan = null; + Span<byte> imageSpan = null; + for (int i = 0; i < contours.Size; i++) { - previousImage = this[layer.Index - 1].LayerMat; - previousSpan = previousImage.GetPixelSpan<byte>(); - imageSpan = image.GetPixelSpan<byte>(); - } - - var rect = CvInvoke.BoundingRectangle(contours[i]); - List<Point> points = new List<Point>(); - uint pixelsSupportingIsland = 0; + if ((int) arr.GetValue(0, i, 2) == -1 && (int) arr.GetValue(0, i, 3) != -1) continue; + if (ReferenceEquals(previousImage, null)) + { + previousImage = this[layer.Index - 1].LayerMat; + previousSpan = previousImage.GetPixelSpan<byte>(); + imageSpan = image.GetPixelSpan<byte>(); + } - using (Mat contourImage = image.CloneBlank()) - { - CvInvoke.DrawContours(contourImage, contours, i, new MCvScalar(255), -1); - var contourImageSpan = contourImage.GetPixelSpan<byte>(); + var rect = CvInvoke.BoundingRectangle(contours[i]); + List<Point> points = new List<Point>(); + uint pixelsSupportingIsland = 0; - for (int y = rect.Y; y < rect.Bottom; y++) + using (Mat contourImage = image.CloneBlank()) { - for (int x = rect.X; x < rect.Right; x++) - { - //int pixel = image.GetPixelPos(x, y); - int pixel = step * y + x; - if (imageSpan[pixel] < minIslandPixelToCheck) - continue; // Low brightness, ignore - if (contourImageSpan[pixel] != 255) continue; // Not inside contour, ignore - - //if (CvInvoke.PointPolygonTest(contours[i], new PointF(x, y), false) < 0) continue; // Out of contour SLOW! - //Debug.WriteLine($"Layer: {layer.Index}, Coutour: {i}, X:{x} Y:{y}"); - points.Add(new Point(x, y)); + CvInvoke.DrawContours(contourImage, contours, i, new MCvScalar(255), -1); + var contourImageSpan = contourImage.GetPixelSpan<byte>(); - if (previousSpan[pixel] >= minIslandSupportPixelColor) + for (int y = rect.Y; y < rect.Bottom; y++) + { + for (int x = rect.X; x < rect.Right; x++) { - pixelsSupportingIsland++; + //int pixel = image.GetPixelPos(x, y); + int pixel = step * y + x; + if (imageSpan[pixel] < minIslandPixelToCheck) + continue; // Low brightness, ignore + if (contourImageSpan[pixel] != 255) + continue; // Not inside contour, ignore + + //if (CvInvoke.PointPolygonTest(contours[i], new PointF(x, y), false) < 0) continue; // Out of contour SLOW! + //Debug.WriteLine($"Layer: {layer.Index}, Coutour: {i}, X:{x} Y:{y}"); + points.Add(new Point(x, y)); + + if (previousSpan[pixel] >= minIslandSupportPixelColor) + { + pixelsSupportingIsland++; + } } } } - } - if (points.Count == 0) continue; - if (pixelsSupportingIsland >= requiredPixelsToSupportIsland) continue; // Not a island, bounding is strong, i think... - if (pixelsSupportingIsland > 0 && points.Count < requiredPixelsToSupportIsland && - pixelsSupportingIsland >= Math.Max(1, points.Count / 2)) continue; // Not a island, but maybe weak bounding... + if (points.Count == 0) continue; + if (pixelsSupportingIsland >= requiredPixelsToSupportIsland) + continue; // Not a island, bounding is strong, i think... + if (pixelsSupportingIsland > 0 && points.Count < requiredPixelsToSupportIsland && + pixelsSupportingIsland >= Math.Max(1, points.Count / 2)) + continue; // Not a island, but maybe weak bounding... - var issue = new LayerIssue(layer, LayerIssue.IssueType.Island, points.ToArray(), rect); - result.AddOrUpdate(layer.Index, new List<LayerIssue>{ issue }, - (layerIndex, list) => - { - list.Add(issue); - return list; - }); - } - else //if ((int) arr.GetValue(0, i, 2) == -1 && (int) arr.GetValue(0, i, 3) != -1) // Hollow area - { - listHollowArea.Add(new LayerHollowArea(contours[i].ToArray(), - CvInvoke.BoundingRectangle(contours[i]), - layer.Index == 0 || layer.Index == Count - 1 - ? LayerHollowArea.AreaType.Drain - : LayerHollowArea.AreaType.Unknown)); - - if (listHollowArea.Count > 0) - layerHollowAreas.TryAdd(layer.Index, listHollowArea); + var issue = new LayerIssue(layer, LayerIssue.IssueType.Island, points.ToArray(), rect); + result.AddOrUpdate(layer.Index, new List<LayerIssue> {issue}, + (layerIndex, list) => + { + list.Add(issue); + return list; + }); + } + + contours.Dispose(); + hierarchy.Dispose(); + previousImage?.Dispose(); } - } - hierarchy.Dispose(); - previousImage?.Dispose(); - } + }); }); - for (uint layerIndex = 1; layerIndex < Count - 1; layerIndex++) // Ignore first and last layers, always drains + var layerHollowAreas = new ConcurrentDictionary<uint, List<LayerHollowArea>>(); + var task2 = new TaskFactory().StartNew(() => { - if (!layerHollowAreas.TryGetValue(layerIndex, out var areas)) continue; // No hollow areas in this layer, ignore + // Detect contours + Parallel.ForEach(this, + //new ParallelOptions{MaxDegreeOfParallelism = 1}, + layer => + { + using (var image = layer.LayerMat) + { + CvInvoke.Threshold(image, image, 254, 255, ThresholdType.Binary); - byte areaCount = 0; - //foreach (var area in areas) - Parallel.ForEach(areas, area => - { - if (area.Type != LayerHollowArea.AreaType.Unknown) return; // processed, ignore - area.Type = LayerHollowArea.AreaType.Trap; + var listHollowArea = new List<LayerHollowArea>(); - areaCount++; + using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) + { + using (Mat hierarchy = new Mat()) + { + + CvInvoke.FindContours(image, contours, hierarchy, RetrType.Ccomp, + ChainApproxMethod.ChainApproxSimple); - List<LayerHollowArea> linkedAreas = new List<LayerHollowArea>(); + var arr = hierarchy.GetData(); + // + //hierarchy[i][0]: the index of the next contour of the same level + //hierarchy[i][1]: the index of the previous contour of the same level + //hierarchy[i][2]: the index of the first child + //hierarchy[i][3]: the index of the parent + // + + for (int i = 0; i < contours.Size; i++) + { + if ((int) arr.GetValue(0, i, 2) != -1 || (int) arr.GetValue(0, i, 3) == -1) + continue; + listHollowArea.Add(new LayerHollowArea(contours[i].ToArray(), + CvInvoke.BoundingRectangle(contours[i]), + layer.Index == 0 || layer.Index == Count - 1 + ? LayerHollowArea.AreaType.Drain + : LayerHollowArea.AreaType.Unknown)); + + if (listHollowArea.Count > 0) + layerHollowAreas.TryAdd(layer.Index, listHollowArea); + } + } + } + } + }); - for (sbyte dir = 1; dir >= -1 && area.Type != LayerHollowArea.AreaType.Drain; dir -= 2) - //Parallel.ForEach(dirs, new ParallelOptions {MaxDegreeOfParallelism = 2}, dir => + + for (uint layerIndex = 1; layerIndex < Count - 1; layerIndex++) // Ignore first and last layers, always drains + { + if (!layerHollowAreas.TryGetValue(layerIndex, out var areas)) + continue; // No hollow areas in this layer, ignore + + byte areaCount = 0; + //foreach (var area in areas) + + Parallel.ForEach(from t in areas where t.Type == LayerHollowArea.AreaType.Unknown select t, area => { - Queue<LayerHollowArea> queue = new Queue<LayerHollowArea>(); - queue.Enqueue(area); - area.Processed = false; - int nextLayerIndex = (int)layerIndex; - while (queue.Count > 0 && area.Type != LayerHollowArea.AreaType.Drain) + //if (area.Type != LayerHollowArea.AreaType.Unknown) return; // processed, ignore + area.Type = LayerHollowArea.AreaType.Trap; + + areaCount++; + + List<LayerHollowArea> linkedAreas = new List<LayerHollowArea>(); + + for (sbyte dir = 1; dir >= -1 && area.Type != LayerHollowArea.AreaType.Drain; dir -= 2) + //Parallel.ForEach(new sbyte[] {1, -1}, new ParallelOptions {MaxDegreeOfParallelism = 2}, dir => { - LayerHollowArea checkArea = queue.Dequeue(); - if (checkArea.Processed) continue; - checkArea.Processed = true; - nextLayerIndex += dir; - //Debug.WriteLine($"Area Count: {areaCount} | Layer: {layerIndex} | Next Layer: {nextLayerIndex} | Dir: {dir}"); - if (nextLayerIndex < 0 && nextLayerIndex >= Count) - break; // Exhausted layers - bool haveNextAreas = - layerHollowAreas.TryGetValue((uint)nextLayerIndex, out var nextAreas); - - using (var image = this[nextLayerIndex].LayerMat) + Queue<LayerHollowArea> queue = new Queue<LayerHollowArea>(); + queue.Enqueue(area); + area.Processed = false; + int nextLayerIndex = (int) layerIndex; + while (queue.Count > 0 && area.Type != LayerHollowArea.AreaType.Drain) { - var span = image.GetPixelSpan<byte>(); - using (var emguImage = image.CloneBlank()) + LayerHollowArea checkArea = queue.Dequeue(); + if (checkArea.Processed) continue; + checkArea.Processed = true; + nextLayerIndex += dir; + Debug.WriteLine($"Area Count: {areaCount} | Layer: {layerIndex} | Next Layer: {nextLayerIndex} | Dir: {dir}"); + if (nextLayerIndex < 0 && nextLayerIndex >= Count) + break; // Exhausted layers + bool haveNextAreas = layerHollowAreas.TryGetValue((uint) nextLayerIndex, out var nextAreas); + List<LayerHollowArea> intersectingAreas = new List<LayerHollowArea>(); + + using (var image = this[nextLayerIndex].LayerMat) { - //emguImage.FillConvexPoly(checkArea.Contour, new Gray(255)); - CvInvoke.DrawContours(emguImage, new VectorOfVectorOfPoint(new VectorOfPoint(checkArea.Contour)), -1, new MCvScalar(255), -1); + var span = image.GetPixelSpan<byte>(); + using (var emguImage = image.CloneBlank()) + { + using(var vec = new VectorOfVectorOfPoint(new VectorOfPoint(checkArea.Contour))) + { + CvInvoke.DrawContours(emguImage, vec, -1, new MCvScalar(255), -1); + } - bool exitPixelLoop = false; - uint blackCount = 0; + if (haveNextAreas) + { + foreach (var nextArea in nextAreas) + { + if (!checkArea.BoundingRectangle.IntersectsWith(nextArea.BoundingRectangle)) continue; - var spanContour = emguImage.GetPixelSpan<byte>(); - for (int y = checkArea.BoundingRectangle.Y; - y <= checkArea.BoundingRectangle.Bottom && - area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop; - y++) - { - for (int x = checkArea.BoundingRectangle.X; - x <= checkArea.BoundingRectangle.Right && + intersectingAreas.Add(nextArea); + /*CvInvoke.DrawContours(emguImage, + new VectorOfVectorOfPoint(new VectorOfPoint(nextArea.Contour)), + -1, + new MCvScalar(intersectingAreas.Count), -1);*/ + } + if (intersectingAreas.Count == 0) + { + haveNextAreas = false; + } + } + + bool exitPixelLoop = false; + uint blackCount = 0; + + var spanContour = emguImage.GetPixelSpan<byte>(); + for (int y = checkArea.BoundingRectangle.Y; + y <= checkArea.BoundingRectangle.Bottom && area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop; - x++) + y++) { - int pixelPos = image.GetPixelPos(x, y); + int pixelPos = image.GetPixelPos(checkArea.BoundingRectangle.X, y)-1; + for (int x = checkArea.BoundingRectangle.X; + x <= checkArea.BoundingRectangle.Right && + area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop; + x++) + { + pixelPos++; - if (spanContour[pixelPos] != 255) continue; - if (span[pixelPos] > 30) continue; - blackCount++; + if (spanContour[pixelPos] == 0) continue; // No contour + if (span[pixelPos] > 30) continue; // Threshold to ignore white area + blackCount++; - if (haveNextAreas - ) // Have areas, can be on same area path or not - { - foreach (var nextArea in nextAreas) + if (haveNextAreas) // Have areas, can be on same area path or not { - if (!(CvInvoke.PointPolygonTest( - new VectorOfPoint(nextArea.Contour), - new PointF(x, y), false) >= 0)) continue; - if (nextArea.Type == LayerHollowArea.AreaType.Drain - ) // Found a drain, stop query + /*int i = spanContour[pixelPos] - 1; + if (i == -1 || i >= 254) + continue; + + //if(queue.Contains(intersectingAreas[i])) continue; + //Debug.WriteLine($"BlackCount: {blackCount}, pixel color: {i}, layerindex: {layerIndex}"); + + if (intersectingAreas[i].Type == LayerHollowArea.AreaType.Drain) // Found a drain, stop query { area.Type = LayerHollowArea.AreaType.Drain; } else { - queue.Enqueue(nextArea); + queue.Enqueue(intersectingAreas[i]); } - linkedAreas.Add(nextArea); + linkedAreas.Add(intersectingAreas[i]); exitPixelLoop = true; + break;*/ + foreach (var nextAreaCheck in intersectingAreas) + { + using (var vec = new VectorOfPoint(nextAreaCheck.Contour)) + { + //Debug.WriteLine(CvInvoke.PointPolygonTest(vec, new PointF(x, y), false)); + if (CvInvoke.PointPolygonTest(vec, new PointF(x, y), false) < 0) continue; + } + + if (nextAreaCheck.Type == LayerHollowArea.AreaType.Drain + ) // Found a drain, stop query + { + area.Type = LayerHollowArea.AreaType.Drain; + exitPixelLoop = true; + } + else + { + queue.Enqueue(area); + } + + linkedAreas.Add(nextAreaCheck); + intersectingAreas.Remove(nextAreaCheck); + haveNextAreas = intersectingAreas.Count > 0; + break; + //exitPixelLoop = true; + //break; + + } + } + else if (blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) // Black pixel without next areas = Drain + { + area.Type = LayerHollowArea.AreaType.Drain; + exitPixelLoop = true; break; } - } - else if (blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) // Black pixel without next areas = Drain - { - area.Type = LayerHollowArea.AreaType.Drain; - exitPixelLoop = true; - break; - } - } // X loop - } // Y loop + } // X loop + } // Y loop - if (queue.Count == 0 && blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) - { + if (queue.Count == 0 && blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) + { - area.Type = LayerHollowArea.AreaType.Drain; - } + area.Type = LayerHollowArea.AreaType.Drain; + } - } // Dispose emgu image - } // Dispose image - } // Areas loop - } // Dir layer loop + } // Dispose emgu image + } // Dispose image + } // Areas loop + } // Dir layer loop - foreach (var linkedArea in linkedAreas) // Update linked areas - { - linkedArea.Type = area.Type; - } - }); - } + foreach (var linkedArea in linkedAreas) // Update linked areas + { + linkedArea.Type = area.Type; + } + }); + } + }); + + task1.Wait(); // Islands & bounds + task2.Wait(); // Resin trap for (uint layerIndex = 0; layerIndex < Count; layerIndex++) { if (!layerHollowAreas.TryGetValue(layerIndex, out var list)) continue; - if (list.Count > 0) + if (list.Count == 0) continue; + foreach (var issue in + from area + in list + where area.Type == LayerHollowArea.AreaType.Trap + select new LayerIssue(this[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle)) { - foreach (var area in list) + result.AddOrUpdate(layerIndex, new List<LayerIssue> {issue}, (u, listIssues) => { - if (area.Type == LayerHollowArea.AreaType.Trap) - { - var issue = new LayerIssue(this[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle); - result.AddOrUpdate(layerIndex, new List<LayerIssue> {issue}, (u, listIssues) => - { - listIssues.Add(issue); - return listIssues; - }); - //issuesHollow.Add(); - } - } - - /*if (issuesHollow.Count > 0) - { - if (Issues.TryGetValue(layerIndex, out var currentIssue)) - { - currentIssue.AddRange(issuesHollow); - } - else - { - Issues.Add(layerIndex, issuesHollow); - } - }*/ + listIssues.Add(issue); + return listIssues; + }); } } diff --git a/UVtools.Core/PHZFile.cs b/UVtools.Core/PHZFile.cs index 966ea1f..34487a1 100644 --- a/UVtools.Core/PHZFile.cs +++ b/UVtools.Core/PHZFile.cs @@ -20,7 +20,6 @@ using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/PWSFile.cs b/UVtools.Core/PWSFile.cs index 342c59d..2cf034a 100644 --- a/UVtools.Core/PWSFile.cs +++ b/UVtools.Core/PWSFile.cs @@ -16,7 +16,6 @@ using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/SL1File.cs b/UVtools.Core/SL1File.cs index bf3d4bd..0ddafbd 100644 --- a/UVtools.Core/SL1File.cs +++ b/UVtools.Core/SL1File.cs @@ -16,7 +16,6 @@ using System.Reflection; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 7de4af6..7f4291a 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -11,6 +11,8 @@ <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> <Version>0.6</Version> + <Copyright>Copyright © 2020 PTRTECH</Copyright> + <PackageIcon>UVtools.png</PackageIcon> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -30,6 +32,10 @@ <Pack>True</Pack> <PackagePath></PackagePath> </None> + <None Include="..\UVtools.GUI\UVtools.png"> + <Pack>True</Pack> + <PackagePath></PackagePath> + </None> <None Include="MatBytes.cs" /> </ItemGroup> diff --git a/UVtools.Core/ZCodexFile.cs b/UVtools.Core/ZCodexFile.cs index 7a2eda5..7cee01b 100644 --- a/UVtools.Core/ZCodexFile.cs +++ b/UVtools.Core/ZCodexFile.cs @@ -17,7 +17,6 @@ using Emgu.CV.CvEnum; using Emgu.CV.Util; using Newtonsoft.Json; using UVtools.Core.Extensions; -using UVtools.Parser; namespace UVtools.Core { diff --git a/UVtools.GUI/Forms/FrmAbout.cs b/UVtools.GUI/Forms/FrmAbout.cs index 509c035..feeb862 100644 --- a/UVtools.GUI/Forms/FrmAbout.cs +++ b/UVtools.GUI/Forms/FrmAbout.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; using UVtools.Core; -using UVtools.Parser; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/Forms/FrmInputBox.cs b/UVtools.GUI/Forms/FrmInputBox.cs index ca7004b..7d014dc 100644 --- a/UVtools.GUI/Forms/FrmInputBox.cs +++ b/UVtools.GUI/Forms/FrmInputBox.cs @@ -10,7 +10,6 @@ using System; using System.Globalization; using System.Windows.Forms; using UVtools.Core; -using UVtools.Parser; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs index 7199973..6d74a8b 100644 --- a/UVtools.GUI/FrmMain.cs +++ b/UVtools.GUI/FrmMain.cs @@ -7,7 +7,6 @@ */ using System; using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; @@ -24,7 +23,6 @@ using Emgu.CV.Util; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.GUI.Forms; -using UVtools.Parser; namespace UVtools.GUI { @@ -592,11 +590,11 @@ namespace UVtools.GUI -1, new MCvScalar(255), -1); - CvInvoke.DrawContours(image, + /*CvInvoke.DrawContours(image, new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)), -1, new MCvScalar(255), - 2); + 2);*/ } } } @@ -942,7 +940,7 @@ namespace UVtools.GUI { var contours = new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)); CvInvoke.DrawContours(image, contours, -1, new MCvScalar(255), -1); - CvInvoke.DrawContours(image, contours, -1, new MCvScalar(255), 2); + //CvInvoke.DrawContours(image, contours, -1, new MCvScalar(255), 2); edited = true; } @@ -1185,14 +1183,7 @@ namespace UVtools.GUI if (!pbLayer.IsPointInImage(e.Location)) return; var location = pbLayer.PointToImage(e.Location); - if (Control.ModifierKeys == Keys.Shift) - { - DrawPixel(false, location); - } - else - { - DrawPixel(true, location); - } + DrawPixel(Control.ModifierKeys != Keys.Shift, location); SlicerFile[ActualLayer].LayerMat = ActualLayerImage; } @@ -1710,39 +1701,41 @@ namespace UVtools.GUI using (Mat grayscale = new Mat()) { CvInvoke.Threshold(ActualLayerImage, grayscale, 1, 255, ThresholdType.Binary); - VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); - Mat hierarchy = new Mat(); - - CvInvoke.FindContours(grayscale, contours, hierarchy, RetrType.Ccomp, - ChainApproxMethod.ChainApproxSimple); - - /* - * hierarchy[i][0]: the index of the next contour of the same level - * hierarchy[i][1]: the index of the previous contour of the same level - * hierarchy[i][2]: the index of the first child - * hierarchy[i][3]: the index of the parent - */ - var arr = hierarchy.GetData(); - for (int i = 0; i < contours.Size; i++) + using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) { - //if ((int)arr.GetValue(0, i, 2) == -1 && (int)arr.GetValue(0, i, 3) > -1) continue; - Debug.WriteLine($"[0] {arr.GetValue(0, i, 0)}"); - Debug.WriteLine($"[1] {arr.GetValue(0, i, 1)}"); - Debug.WriteLine($"[2] {arr.GetValue(0, i, 2)}"); - Debug.WriteLine($"[3] {arr.GetValue(0, i, 3)}"); - //if ((int)arr.GetValue(0, i, 2) > -1 || (int)arr.GetValue(0, i, 3) > -1) continue; - //if (((int)arr.GetValue(0, i, 2) > -1 && (int)arr.GetValue(0, i, 3) > -1)) continue; - - // if ((int) arr.GetValue(0, i, 3) >= 0) continue; - if ((int)arr.GetValue(0, i, 2) != -1 || (int)arr.GetValue(0, i, 3) == -1){ - //if ((int)arr.GetValue(0, i, 2) == -1 && (int)arr.GetValue(0, i, 3) != -1){ - var r = CvInvoke.BoundingRectangle(contours[i]); - CvInvoke.Rectangle(ActualLayerImageBgr, r, new MCvScalar(0, 0, 255), 5); - CvInvoke.DrawContours(ActualLayerImageBgr, contours, i, new MCvScalar(125, 125, 125), - -1); + using (Mat hierarchy = new Mat()) + { + CvInvoke.FindContours(grayscale, contours, hierarchy, RetrType.Ccomp, + ChainApproxMethod.ChainApproxSimple); + + /* + * hierarchy[i][0]: the index of the next contour of the same level + * hierarchy[i][1]: the index of the previous contour of the same level + * hierarchy[i][2]: the index of the first child + * hierarchy[i][3]: the index of the parent + */ + var arr = hierarchy.GetData(); + for (int i = 0; i < contours.Size; i++) + { + //if ((int)arr.GetValue(0, i, 2) == -1 && (int)arr.GetValue(0, i, 3) > -1) continue; + /*Debug.WriteLine($"[0] {arr.GetValue(0, i, 0)}"); + Debug.WriteLine($"[1] {arr.GetValue(0, i, 1)}"); + Debug.WriteLine($"[2] {arr.GetValue(0, i, 2)}"); + Debug.WriteLine($"[3] {arr.GetValue(0, i, 3)}");*/ + //if ((int)arr.GetValue(0, i, 2) > -1 || (int)arr.GetValue(0, i, 3) > -1) continue; + //if (((int)arr.GetValue(0, i, 2) > -1 && (int)arr.GetValue(0, i, 3) > -1)) continue; + + // if ((int) arr.GetValue(0, i, 3) >= 0) continue; + if ((int)arr.GetValue(0, i, 2) != -1 || (int)arr.GetValue(0, i, 3) == -1) + continue; + var r = CvInvoke.BoundingRectangle(contours[i]); + CvInvoke.Rectangle(ActualLayerImageBgr, r, new MCvScalar(0, 0, 255), 5); + CvInvoke.DrawContours(ActualLayerImageBgr, contours, i, new MCvScalar(125, 125, 125), -1); + + //if ((int) arr.GetValue(0, i, 2) == -1 && (int) arr.GetValue(0, i, 3) != -1) + // CvInvoke.DrawContours(ActualLayerImageBgr, contours, i, new MCvScalar(0, 0, 0), -1); + } } - //if ((int) arr.GetValue(0, i, 2) == -1 && (int) arr.GetValue(0, i, 3) != -1) - // CvInvoke.DrawContours(ActualLayerImageBgr, contours, i, new MCvScalar(0, 0, 0), -1); } } #else @@ -1752,8 +1745,6 @@ namespace UVtools.GUI CvInvoke.CvtColor(grayscale, ActualLayerImageBgr, ColorConversion.Gray2Bgr); } #endif - - } else if (tsLayerImageLayerDifference.Checked) { @@ -1809,7 +1800,7 @@ namespace UVtools.GUI if (issue.Type == LayerIssue.IssueType.ResinTrap) { CvInvoke.DrawContours(ActualLayerImageBgr, new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)), -1, new MCvScalar(0, 180, 255), -1); - CvInvoke.DrawContours(ActualLayerImageBgr, new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)), -1, new MCvScalar(0, 0, 255), 1); + //CvInvoke.DrawContours(ActualLayerImageBgr, new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)), -1, new MCvScalar(0, 0, 255), 1); continue; } @@ -2071,6 +2062,7 @@ namespace UVtools.GUI void DrawPixel(bool isAdd, Point location) { + //Stopwatch sw = Stopwatch.StartNew(); //var point = pbLayer.PointToImage(location); int x = location.X; int y = location.Y; @@ -2103,17 +2095,11 @@ namespace UVtools.GUI Bitmap bmp = pbLayer.Image as Bitmap; bmp.SetPixel(location.X, location.Y, color); - /*if (bmp.GetPixel(point.X, point.Y).GetBrightness() == returnif) return; - bmp.SetPixel(point.X, point.Y, color); - ActualLayerImage[point.X, point.Y] = pixelL8; - var newImage = ActualLayerImage.Clone(); - if (tsLayerImageRotate.Checked) - { - newImage.Mutate(mut => mut.Rotate(RotateMode.Rotate270)); - } - SlicerFile[ActualLayer].Image = newImage;*/ + pbLayer.Invalidate(); menuFileSave.Enabled = menuFileSaveAs.Enabled = true; + //sw.Stop(); + //Debug.WriteLine(sw.ElapsedMilliseconds); } @@ -2125,8 +2111,6 @@ namespace UVtools.GUI uint iterationsStart = 0; uint iterationsEnd = 0; bool fade = false; - float iterationSteps = 0; - uint maxIteration = 0; double x = 0; double y = 0; @@ -2154,12 +2138,6 @@ namespace UVtools.GUI layerStart = inputBox.LayerRangeStart; layerEnd = inputBox.LayerRangeEnd; iterationsEnd = inputBox.IterationsEnd; - fade = layerStart != layerEnd && iterationsStart != iterationsEnd && inputBox.IterationsFade; - if (fade) - { - iterationSteps = Math.Abs((float)iterationsStart - iterationsEnd) / (layerEnd - layerStart); - maxIteration = Math.Max(iterationsStart, iterationsEnd); - } } break; @@ -2175,119 +2153,61 @@ namespace UVtools.GUI bool result = false; try { - if (type == Mutation.Mutates.Resize) + switch (type) { - SlicerFile.Resize(layerStart, layerEnd, x / 100.0, y / 100.0, fade); - } - else - { - Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(1, 1)); - Parallel.For(layerStart, layerEnd + 1, layerIndex => - { - var iterations = iterationsStart; - if (fade) - { - // calculate iterations based on range - iterations = iterationsStart < iterationsEnd - ? (uint) (iterationsStart + (layerIndex - layerStart) * iterationSteps) - : (uint) (iterationsStart - (layerIndex - layerStart) * iterationSteps); - - // constrain - iterations = Math.Min(Math.Max(1, iterations), maxIteration); - //Debug.WriteLine($"A Layer: {i} = {iterations}"); - } - - Layer layer = SlicerFile[layerIndex]; - var image = layer.LayerMat; - switch (type) - { - case Mutation.Mutates.Resize: - //var resizedImage = imageEgmu.Resize( (int) (iterationsStart / 100.0 * image.Width), (int) (iterationsEnd / 100.0 * image.Height), Inter.Lanczos4); - //imageEgmu = resizedImage.Copy(new Rectangle(0, 0, image.Width, image.Height)); - break; - case Mutation.Mutates.Solidify: - for (byte pass = 0; pass < 1; pass++) // Passes - { - CvInvoke.Threshold(image, image, 254, 255, ThresholdType.Binary); // Clean AA - - VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); - Mat hierarchy = new Mat(); - - CvInvoke.FindContours(image, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); - - var arr = hierarchy.GetData(); - - /* - * hierarchy[i][0]: the index of the next contour of the same level - * hierarchy[i][1]: the index of the previous contour of the same level - * hierarchy[i][2]: the index of the first child - * hierarchy[i][3]: the index of the parent - */ - for (int i = 0; i < contours.Size; i++) - { - if ((int)arr.GetValue(0, i, 2) != -1 || (int)arr.GetValue(0, i, 3) == -1) continue; - var r = CvInvoke.BoundingRectangle(contours[i]); - //imageEgmu.FillConvexPoly(contours[i].ToArray(), new Gray(255)); - //imageThreshold.FillConvexPoly(contours[i].ToArray(), new Gray(255)); - //imageEgmu.Draw(contours, i, new Gray(255), -1); - CvInvoke.DrawContours(image, contours, i, new MCvScalar(255), -1); - } - - // Attempt to close any tiny region - //imageEgmu = imageEgmu.Dilate(2).Erode(2); - } - - - - break; - case Mutation.Mutates.Erode: - CvInvoke.Erode(image, image, kernel, new Point(-1,-1), (int) iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.Dilate: - CvInvoke.Dilate(image, image, kernel, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.Opening: - CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.Closing: - CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.Gradient: - CvInvoke.MorphologyEx(image, image, MorphOp.Gradient, Program.KernelStar3x3, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.TopHat: - kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(9, 9), - new Point(-1, -1)); - CvInvoke.MorphologyEx(image, image, MorphOp.Tophat, kernel, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.BlackHat: - kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(9, 9), - new Point(-1, -1)); - CvInvoke.MorphologyEx(image, image, MorphOp.Blackhat, kernel, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.HitMiss: - CvInvoke.MorphologyEx(image, image, MorphOp.HitMiss, Program.KernelFindIsolated, new Point(-1, -1), (int)iterations, BorderType.Default, new MCvScalar()); - break; - case Mutation.Mutates.PyrDownUp: - CvInvoke.PyrDown(image, image); - CvInvoke.PyrUp(image, image); - break; - case Mutation.Mutates.SmoothMedian: - CvInvoke.MedianBlur(image, image, (int) iterations); - break; - case Mutation.Mutates.SmoothGaussian: - CvInvoke.GaussianBlur(image, image, new Size((int)iterations, (int)iterations), 2); - break; - } - - layer.LayerMat = image; - }); + case Mutation.Mutates.Resize: + SlicerFile.LayerManager.MutateResize(layerStart, layerEnd, x / 100.0, y / 100.0, fade); + break; + case Mutation.Mutates.Solidify: + SlicerFile.LayerManager.MutateSolidify(layerStart, layerEnd); + break; + case Mutation.Mutates.Erode: + SlicerFile.LayerManager.MutateErode(layerStart, layerEnd, (int) iterationsStart, (int) iterationsEnd, fade); + break; + case Mutation.Mutates.Dilate: + SlicerFile.LayerManager.MutateDilate(layerStart, layerEnd, (int)iterationsStart, (int)iterationsEnd, fade); + break; + case Mutation.Mutates.Opening: + SlicerFile.LayerManager.MutateOpen(layerStart, layerEnd, (int)iterationsStart, (int)iterationsEnd, fade); + break; + case Mutation.Mutates.Closing: + SlicerFile.LayerManager.MutateClose(layerStart, layerEnd, (int)iterationsStart, (int)iterationsEnd, fade); + break; + case Mutation.Mutates.Gradient: + SlicerFile.LayerManager.MutateGradient(layerStart, layerEnd, (int)iterationsStart, (int)iterationsEnd, fade); + break; + /*case Mutation.Mutates.TopHat: + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(9, 9), + new Point(-1, -1)); + CvInvoke.MorphologyEx(image, image, MorphOp.Tophat, kernel, new Point(-1, -1), + (int) iterations, BorderType.Default, new MCvScalar()); + break; + case Mutation.Mutates.BlackHat: + kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(9, 9), + new Point(-1, -1)); + CvInvoke.MorphologyEx(image, image, MorphOp.Blackhat, kernel, new Point(-1, -1), + (int) iterations, BorderType.Default, new MCvScalar()); + break; + case Mutation.Mutates.HitMiss: + CvInvoke.MorphologyEx(image, image, MorphOp.HitMiss, Program.KernelFindIsolated, + new Point(-1, -1), (int) iterations, BorderType.Default, new MCvScalar()); + break;*/ + case Mutation.Mutates.PyrDownUp: + SlicerFile.LayerManager.MutatePyrDownUp(layerStart, layerEnd); + break; + case Mutation.Mutates.SmoothMedian: + SlicerFile.LayerManager.MutateMedianBlur(layerStart, layerEnd, (int)iterationsStart); + break; + case Mutation.Mutates.SmoothGaussian: + SlicerFile.LayerManager.MutateGaussianBlur(layerStart, layerEnd, new Size((int) iterationsStart, (int) iterationsStart)); + break; } result = true; } catch (Exception ex) { + result = false; MessageBox.Show($"{ex.Message}\nPlease try different values for the mutation", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally @@ -2349,11 +2269,6 @@ namespace UVtools.GUI bool result = false; try { - /*Task taskGenericIssues = Task.Factory.StartNew(() => - { - - });*/ - var issues = SlicerFile.LayerManager.GetAllIssues(); Issues = new Dictionary<uint, List<LayerIssue>>(); @@ -2365,194 +2280,6 @@ namespace UVtools.GUI } } - - /*var layerHollowAreas = new ConcurrentDictionary<uint, List<LayerHollowArea>>(); - - Parallel.ForEach(SlicerFile, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layer => - { - using (var image = layer.LayerMat) - { - using (Image<Gray, byte> grayscale = image.ToEmguImage().ThresholdBinary(new Gray(254), new Gray(255))) - { - var listHollowArea = new List<LayerHollowArea>(); - - VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); - Mat hierarchy = new Mat(); - - CvInvoke.FindContours(grayscale, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); - - var arr = hierarchy.GetData(); - // - //hierarchy[i][0]: the index of the next contour of the same level - //hierarchy[i][1]: the index of the previous contour of the same level - //hierarchy[i][2]: the index of the first child - //hierarchy[i][3]: the index of the parent - // - - for (int i = 0; i < contours.Size; i++) - { - if ((int) arr.GetValue(0, i, 2) != -1 || (int) arr.GetValue(0, i, 3) == -1) - continue; - - listHollowArea.Add(new LayerHollowArea(contours[i].ToArray(), CvInvoke.BoundingRectangle(contours[i]), layer.Index == 0 || layer.Index == SlicerFile.LayerCount-1 ? LayerHollowArea.AreaType.Drain : LayerHollowArea.AreaType.Unknown)); - - if (listHollowArea.Count > 0) - layerHollowAreas.TryAdd(layer.Index, listHollowArea); - } - } - } - });*/ - - - - /*for (uint layerIndex = 1; layerIndex < SlicerFile.LayerCount-1; layerIndex++) // Ignore first and last layers, always drains - { - if(!layerHollowAreas.TryGetValue(layerIndex, out var areas)) continue; // No hollow areas in this layer, ignore - - byte areaCount = 0; - //foreach (var area in areas) - Parallel.ForEach(areas, area => - { - if (area.Type != LayerHollowArea.AreaType.Unknown) return; // processed, ignore - area.Type = LayerHollowArea.AreaType.Trap; - - areaCount++; - - List<LayerHollowArea> linkedAreas = new List<LayerHollowArea>(); - - for (sbyte dir = 1; dir >= -1 && area.Type != LayerHollowArea.AreaType.Drain; dir -= 2) - //Parallel.ForEach(dirs, new ParallelOptions {MaxDegreeOfParallelism = 2}, dir => - { - Queue<LayerHollowArea> queue = new Queue<LayerHollowArea>(); - queue.Enqueue(area); - area.Processed = false; - int nextLayerIndex = (int) layerIndex; - while (queue.Count > 0 && area.Type != LayerHollowArea.AreaType.Drain) - { - LayerHollowArea checkArea = queue.Dequeue(); - if (checkArea.Processed) continue; - checkArea.Processed = true; - nextLayerIndex += dir; - Debug.WriteLine( - $"Area Count: {areaCount} | Layer: {layerIndex} | Next Layer: {nextLayerIndex} | Dir: {dir}"); - if (nextLayerIndex < 0 && nextLayerIndex >= SlicerFile.LayerCount) - break; // Exhaust layers - bool haveNextAreas = - layerHollowAreas.TryGetValue((uint) nextLayerIndex, out var nextAreas); - - using (var image = SlicerFile[nextLayerIndex].LayerMat) - { - using (var emguImage = new Image<Gray, byte>(ActualLayerImage.Width, - ActualLayerImage.Height)) - { - //emguImage.FillConvexPoly(checkArea.Contour, new Gray(255)); - emguImage.Draw(new VectorOfVectorOfPoint(new VectorOfPoint(checkArea.Contour)), -1, - new Gray(255), -1); - - bool exitPixelLoop = false; - uint blackCount = 0; - - byte[,,] data = emguImage.Data; - for (int y = checkArea.BoundingRectangle.Y; - y <= checkArea.BoundingRectangle.Bottom && - area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop; - y++) - { - for (int x = checkArea.BoundingRectangle.X; - x <= checkArea.BoundingRectangle.Right && - area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop; - x++) - { - - if (data[y, x, 0] != 255) continue; - if (span[y * image.Width + x].PackedValue > 30) continue; - blackCount++; - - if (haveNextAreas - ) // Have areas, can be on same area path or not - { - foreach (var nextArea in nextAreas) - { - if (!(CvInvoke.PointPolygonTest( - new VectorOfPoint(nextArea.Contour), - new PointF(x, y), false) >= 0)) continue; - if (nextArea.Type == LayerHollowArea.AreaType.Drain - ) // Found a drain, stop query - { - area.Type = LayerHollowArea.AreaType.Drain; - } - else - { - queue.Enqueue(nextArea); - } - - linkedAreas.Add(nextArea); - - exitPixelLoop = true; - break; - } - } - else if(blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) // Black pixel without next areas = Drain - { - area.Type = LayerHollowArea.AreaType.Drain; - exitPixelLoop = true; - break; - } - } // X loop - } // Y loop - - if (queue.Count == 0 && blackCount > Math.Min(checkArea.Contour.Length / 2, 10)) - { - - area.Type = LayerHollowArea.AreaType.Drain; - } - - } // Dispose emgu image - } // Dispose image - } // Areas loop - } // Dir layer loop - - foreach (var linkedArea in linkedAreas) // Update linked areas - { - linkedArea.Type = area.Type; - } - }); - } - - taskGenericIssues.Wait(); - - for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) - { - if (!layerHollowAreas.TryGetValue(layerIndex, out var list)) continue; - if (list.Count > 0) - { - var issuesHollow = new List<LayerIssue>(); - - foreach (var area in list) - { - if (area.Type == LayerHollowArea.AreaType.Trap) - { - issuesHollow.Add(new LayerIssue(SlicerFile[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle)); - } - } - - if (issuesHollow.Count > 0) - { - if (Issues.TryGetValue(layerIndex, out var currentIssue)) - { - currentIssue.AddRange(issuesHollow); - } - else - { - Issues.Add(layerIndex, issuesHollow); - } - } - } - }*/ - - result = true; } catch (Exception ex) diff --git a/UVtools.GUI/Program.cs b/UVtools.GUI/Program.cs index b818ef9..675c1fa 100644 --- a/UVtools.GUI/Program.cs +++ b/UVtools.GUI/Program.cs @@ -14,7 +14,6 @@ using System.Windows.Forms; using ApplicationManagement; using Emgu.CV; using UVtools.Core; -using UVtools.Parser; using UVtools.GUI.Forms; namespace UVtools.GUI diff --git a/UVtools.Parser/About.cs b/UVtools.Parser/About.cs deleted file mode 100644 index ee7fa78..0000000 --- a/UVtools.Parser/About.cs +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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. - */ - -namespace UVtools.Parser -{ - public static class About - { - public static string Software = "UVtools"; - public static string Author = "Tiago Conceição"; - public static string Company = "PTRTECH"; - public static string Website = "https://github.com/sn4k3/UVtools"; - public static string Donate = "https://paypal.me/SkillTournament"; - } -} diff --git a/UVtools.Parser/CWSFile.cs b/UVtools.Parser/CWSFile.cs deleted file mode 100644 index 9b8bca3..0000000 --- a/UVtools.Parser/CWSFile.cs +++ /dev/null @@ -1,476 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.ComponentModel; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection; -using System.Text; -using UVtools.Parser.Extensions; - -namespace UVtools.Parser -{ - public class CWSFile : FileFormat - { - #region Constants - - public const string GCodeStart = "G28 ; Auto Home{0}" + - "G21 ;Set units to be mm{0}" + - "G91 ;Relative Positioning{0}" + - "M17 ;Enable motors{0}" + - "<Slice> Blank{0}" + - "M106 S0{0}{0}"; - - public const string GCodeEnd = "M106 S0{0}" + - "G1 Z{1}{0}" + - "{0}M18 ;Disable Motors{0}" + - ";<Completed>{0}"; - - public const string GCodeKeywordSlice = ";<Slice>"; - public const string GCodeKeywordSliceBlank = ";<Slice> Blank"; - public const string GCodeKeywordDelay = ";<Delay>"; - #endregion - - #region Sub Classes - - public class Output - { - // ;(****Build and Slicing Parameters****) - [DisplayName("Pix per mm X")] public float PixPermmX { get; set; } = 19.324f; - [DisplayName("Pix per mm Y")] public float PixPermmY { get; set; } = 19.324f; - [DisplayName("X Resolution")] public ushort XResolution { get; set; } - [DisplayName("Y Resolution")] public ushort YResolution { get; set; } - [DisplayName("Layer Thickness")] public float LayerThickness { get; set; } - [DisplayName("Layer Time")] public uint LayerTime { get; set; } = 5500; - [DisplayName("Render Outlines")] public bool RenderOutlines { get; set; } = false; - [DisplayName("Outline Width Inset")] public ushort OutlineWidthInset { get; set; } = 2; - [DisplayName("Outline Width Outset")] public ushort OutlineWidthOutset { get; set; } = 0; - [DisplayName("Bottom Layers Time")] public uint BottomLayersTime { get; set; } = 35000; - [DisplayName("Number of Bottom Layers")] public ushort NumberBottomLayers { get; set; } = 3; - [DisplayName("Blanking Layer Time")] public uint BlankingLayerTime { get; set; } - [DisplayName("BuildDirection")] public string BuildDirection { get; set; } = "Bottom_Up"; - [DisplayName("Lift Distance")] public float LiftDistance { get; set; } = 4; - [DisplayName("Slide/Tilt Value")] public byte TiltValue { get; set; } - [DisplayName("Use Mainlift GCode Tab")] public bool UseMainliftGCodeTab { get; set; } - [DisplayName("Anti Aliasing")] public bool AntiAliasing { get; set; } = true; - [DisplayName("Anti Aliasing Value")] public float AntiAliasingValue { get; set; } = 2; - [DisplayName("Z Lift Feed Rate")] public float ZLiftFeedRate { get; set; } = 120; - [DisplayName("Z Bottom Lift Feed Rate")] public float ZBottomLiftFeedRate { get; set; } = 120; - [DisplayName("Z Lift Retract Rate")] public float ZLiftRetractRate { get; set; } = 120; - [DisplayName("Flip X")] public bool FlipX { get; set; } - [DisplayName("Flip Y")] public bool FlipY { get; set; } - [DisplayName("Number of Slices")] public uint LayersNum { get; set; } - - // ;(****Machine Configuration ******) - [DisplayName("Platform X Size")] public float PlatformXSize { get; set; } - [DisplayName("Platform Y Size")] public float PlatformYSize { get; set; } - [DisplayName("Platform Z Size")] public float PlatformZSize { get; set; } - [DisplayName("Max X Feedrate")] public ushort MaxXFeedrate { get; set; } = 200; - [DisplayName("Max Y Feedrate")] public ushort MaxYFeedrate { get; set; } = 200; - [DisplayName("Max Z Feedrate")] public ushort MaxZFeedrate { get; set; } = 200; - [DisplayName("Machine Type")] public string MachineType { get; set; } = "UV_LCD"; - - // ;(****UVtools Configuration ******) - [DisplayName("Bottom Layer Light PWM")] public byte BottomLayerLightPWM { get; set; } = 255; - [DisplayName("Layer Light PWM")] public byte LayerLightPWM { get; set; } = 255; - } - - public class Slice - { - [DisplayName("xppm")] public float Xppm { get; set; } = 19.324f; - [DisplayName("yppm")] public float Yppm { get; set; } = 19.324f; - [DisplayName("xres")] public ushort Xres { get; set; } - [DisplayName("yres")] public ushort Yres { get; set; } - [DisplayName("thickness")] public float Thickness { get; set; } - [DisplayName("layers_num")] public uint LayersNum { get; set; } - [DisplayName("head_layers_num")] public ushort HeadLayersNum { get; set; } = 3; - [DisplayName("layers_expo_ms")] public uint LayersExpoMs { get; set; } = 5500; - [DisplayName("head_layers_expo_ms")] public uint HeadLayersExpoMs { get; set; } = 35000; - [DisplayName("wait_before_expo_ms")] public uint WaitBeforeExpoMs { get; set; } = 2000; - [DisplayName("lift_distance")] public float LiftDistance { get; set; } = 4; - [DisplayName("lift_up_speed")] public float LiftUpSpeed { get; set; } = 120; - [DisplayName("lift_down_speed")] public float LiftDownSpeed { get; set; } = 120; - [DisplayName("lift_when_finished")] public byte LiftWhenFinished { get; set; } = 80; - } - - #endregion - - #region Properties - public Slice SliceSettings { get; } = new Slice(); - public Output OutputSettings { get; } = new Output(); - - - public override FileFormatType FileType => FileFormatType.Archive; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("cws", "NovaMaker CWS Files") - }; - - public override Type[] ConvertToFormats { get; } = null; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - - PrintParameterModifier.LiftHeight, - PrintParameterModifier.RetractSpeed, - PrintParameterModifier.LiftSpeed, - - PrintParameterModifier.BottomLightPWM, - PrintParameterModifier.LightPWM, - }; - - public override byte ThumbnailsCount { get; } = 0; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = null; - - public override uint ResolutionX => SliceSettings.Xres; - - public override uint ResolutionY => SliceSettings.Yres; - public override byte AntiAliasing => (byte) OutputSettings.AntiAliasingValue; - - public override float LayerHeight => SliceSettings.Thickness; - - public override ushort InitialLayerCount => SliceSettings.HeadLayersNum; - - public override float InitialExposureTime => SliceSettings.HeadLayersExpoMs / 1000f; - - public override float LayerExposureTime => SliceSettings.LayersExpoMs / 1000f; - - public override float LiftHeight => SliceSettings.LiftDistance; - - public override float LiftSpeed => SliceSettings.LiftDownSpeed; - - public override float RetractSpeed => OutputSettings.ZLiftRetractRate; - - public override float PrintTime => 0; - - public override float UsedMaterial => 0; - - public override float MaterialCost => 0; - - public override string MaterialName => string.Empty; - - public override string MachineName => "Unknown"; - - public override object[] Configs => new object[] { SliceSettings, OutputSettings}; - #endregion - - #region Methods - - public override void Clear() - { - base.Clear(); - GCode = null; - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Update)) - { - string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; - var entry = outputFile.GetPutFile("slice.conf"); - var stream = entry.Open(); - stream.SetLength(0); - - using (TextWriter tw = new StreamWriter(stream)) - { - - tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); - tw.WriteLine("# conf version 1.0"); - tw.WriteLine(""); - - foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}"); - } - } - - - for(uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - Layer layer = this[layerIndex]; - var layerimagePath = $"{Path.GetFileNameWithoutExtension(fileFullPath)}{layer.Index:D4}.png"; - outputFile.PutFileContent(layerimagePath, layer.RawData); - } - - UpdateGCode(); - outputFile.PutFileContent($"{Path.GetFileNameWithoutExtension(fileFullPath)}.gcode", GCode.ToString()); - } - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) - { - var entry = inputFile.GetEntry("slice.conf"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("slice.conf not found", fileFullPath); - } - - - - using (TextReader tr = new StreamReader(entry.Open())) - { - string line; - while ((line = tr.ReadLine()) != null) - { - if (string.IsNullOrEmpty(line)) continue; - if(line[0] == '#') continue; - - var splitLine = line.Split('='); - if(splitLine.Length < 2) continue; - - foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if(ReferenceEquals(displayNameAttribute, null)) continue; - if(!splitLine[0].Trim().Equals(displayNameAttribute.DisplayName)) continue; - Helpers.SetPropertyValue(propertyInfo, SliceSettings, splitLine[1].Trim()); - } - } - tr.Close(); - } - - entry = inputFile.GetEntry($"{Path.GetFileNameWithoutExtension(fileFullPath)}.gcode"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException($"{Path.GetFileNameWithoutExtension(fileFullPath)}.gcode not found", fileFullPath); - } - - using (TextReader tr = new StreamReader(entry.Open())) - { - string line; - GCode = new StringBuilder(); - while ((line = tr.ReadLine()) != null) - { - GCode.AppendLine(line); - if (string.IsNullOrEmpty(line)) continue; - - if (line[0] != ';') - { - continue; - } - - var splitLine = line.Split('='); - if (splitLine.Length < 2) continue; - - foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - if (!splitLine[0].Trim(' ', ';', '(').Equals(displayNameAttribute.DisplayName)) continue; - Helpers.SetPropertyValue(propertyInfo, OutputSettings, splitLine[1].Trim(' ', ')', 'm', 'n', 's', '/')); - //Debug.WriteLine(splitLine[1].Trim(' ', ')', 'm', 'n', '/')); - } - } - tr.Close(); - } - - - LayerManager = new LayerManager(OutputSettings.LayersNum); - - foreach (var zipArchiveEntry in inputFile.Entries) - { - if (!zipArchiveEntry.Name.EndsWith(".png")) continue; - - // - .png - 4 numbers - string layerStr = zipArchiveEntry.Name.Substring(zipArchiveEntry.Name.Length - 4 - 4, 4); - uint iLayer = uint.Parse(layerStr); - LayerManager[iLayer] = new Layer(iLayer, zipArchiveEntry.Open(), zipArchiveEntry.Name); - } - - } - } - - public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - var baseValue = base.GetValueFromPrintParameterModifier(modifier); - if (!ReferenceEquals(baseValue, null)) return baseValue; - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return OutputSettings.BottomLayerLightPWM; - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return OutputSettings.LayerLightPWM; - - return null; - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - SliceSettings.HeadLayersNum = - OutputSettings.NumberBottomLayers = value.Convert<ushort>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - SliceSettings.HeadLayersExpoMs = - OutputSettings.BottomLayersTime = value.Convert<uint>()*1000; - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - SliceSettings.LayersExpoMs = - OutputSettings.LayerTime = value.Convert<uint>() * 1000; - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - SliceSettings.LiftDistance = - OutputSettings.LiftDistance = value.Convert<byte>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - SliceSettings.LiftUpSpeed = - OutputSettings.ZLiftFeedRate = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - SliceSettings.LiftDownSpeed = - OutputSettings.ZLiftRetractRate = - OutputSettings.ZBottomLiftFeedRate = value.Convert<float>(); - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) - { - OutputSettings.BottomLayerLightPWM = value.Convert<byte>(); - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) - { - OutputSettings.BottomLayerLightPWM = value.Convert<byte>(); - UpdateGCode(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - using (var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update)) - { - string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; - var entry = outputFile.GetPutFile("slice.conf"); - var stream = entry.Open(); - stream.SetLength(0); - - using (TextWriter tw = new StreamWriter(stream)) - { - - tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); - tw.WriteLine("# conf version 1.0"); - tw.WriteLine(""); - - foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}"); - } - } - - - foreach (var zipentry in outputFile.Entries) - { - if (zipentry.Name.EndsWith(".gcode")) - { - zipentry.Delete(); - break; - } - } - outputFile.PutFileContent($"{Path.GetFileNameWithoutExtension(FileFullPath)}.gcode", GCode.ToString()); - - foreach (var layer in this) - { - if (!layer.IsModified) continue; - outputFile.PutFileContent(layer.Filename, layer.RawData); - layer.IsModified = false; - } - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - throw new NotImplementedException(); - } - - private void UpdateGCode() - { - string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; - GCode = new StringBuilder(); - GCode.AppendLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); - GCode.AppendLine("(****Build and Slicing Parameters * ***)"); - - foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - GCode.AppendLine($";({displayNameAttribute.DisplayName.PadRight(24)} = {propertyInfo.GetValue(OutputSettings)})"); - } - GCode.AppendLine(); - GCode.AppendFormat(GCodeStart, Environment.NewLine); - - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - //Layer layer = this[layerIndex]; - GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}"); - GCode.AppendLine($"M106 S{GetInitialLayerValueOrNormal(layerIndex, OutputSettings.BottomLayerLightPWM, OutputSettings.LayerLightPWM)}"); - GCode.AppendLine($"{GCodeKeywordDelay} {GetInitialLayerValueOrNormal(layerIndex, SliceSettings.HeadLayersExpoMs, SliceSettings.LayersExpoMs)}"); - GCode.AppendLine("M106 S0"); - GCode.AppendLine(GCodeKeywordSliceBlank); - GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}"); - GCode.AppendLine($"G1 Z-{LiftHeight - LayerHeight} F{RetractSpeed}"); - GCode.AppendLine($"{GCodeKeywordDelay} {GetInitialLayerValueOrNormal(layerIndex, SliceSettings.HeadLayersExpoMs, SliceSettings.LayersExpoMs)}"); - } - - GCode.AppendFormat(GCodeEnd, Environment.NewLine, SliceSettings.LiftWhenFinished); - - /*GCode = Regex.Replace(GCode, @"Z[+]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", - $"Z{SliceSettings.LiftDistance} F{SliceSettings.LiftUpSpeed}"); - - GCode = Regex.Replace(GCode, @"Z-[-]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", - $"Z-{SliceSettings.LiftDistance - LayerHeight} F{SliceSettings.LiftDownSpeed}");*/ - - } - #endregion - } -} diff --git a/UVtools.Parser/ChituboxFile.cs b/UVtools.Parser/ChituboxFile.cs deleted file mode 100644 index 5c3c1f3..0000000 --- a/UVtools.Parser/ChituboxFile.cs +++ /dev/null @@ -1,1611 +0,0 @@ -/* - * 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. - */ - -// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using BinarySerialization; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace UVtools.Parser -{ - public class ChituboxFile : FileFormat - { - #region Constants - private const uint MAGIC_CBDDLP = 0x12FD0019; - private const uint MAGIC_CBT = 0x12FD0086; - private const ushort REPEATRGB15MASK = 0x20; - - private const byte RLE8EncodingLimit = 0x7d; // 125; - private const ushort RLE16EncodingLimit = 0xFFF; - #endregion - - #region Sub Classes - #region Header - public class Header - { - - /// <summary> - /// Gets a magic number identifying the file type. - /// 0x12fd_0019 for cbddlp - /// 0x12fd_0086 for ctb - /// </summary> - [FieldOrder(0)] public uint Magic { get; set; } - - /// <summary> - /// Gets the software version - /// </summary> - [FieldOrder(1)] public uint Version { get; set; } = 2; - - /// <summary> - /// Gets dimensions of the printer’s X output volume, in millimeters. - /// </summary> - [FieldOrder(2)] public float BedSizeX { get; set; } - - /// <summary> - /// Gets dimensions of the printer’s Y output volume, in millimeters. - /// </summary> - [FieldOrder(3)] public float BedSizeY { get; set; } - - /// <summary> - /// Gets dimensions of the printer’s Z output volume, in millimeters. - /// </summary> - [FieldOrder(4)] public float BedSizeZ { get; set; } - - [FieldOrder(5)] public uint Unknown1 { get; set; } - [FieldOrder(6)] public uint Unknown2 { get; set; } - - /// <summary> - /// Gets the height of the model described by this file, in millimeters. - /// </summary> - [FieldOrder(7)] public float OverallHeightMilimeter { get; set; } - - /// <summary> - /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table. - /// </summary> - [FieldOrder(8)] public float LayerHeightMilimeter { get; set; } - - /// <summary> - /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table. - /// </summary> - [FieldOrder(9)] public float LayerExposureSeconds { get; set; } - - /// <summary> - /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table. - /// </summary> - [FieldOrder(10)] public float BottomExposureSeconds { get; set; } - - /// <summary> - /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(11)] public float LayerOffTime { get; set; } = 1; - - /// <summary> - /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.. - /// </summary> - [FieldOrder(12)] public uint BottomLayersCount { get; set; } = 10; - - /// <summary> - /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images. - /// </summary> - [FieldOrder(13)] public uint ResolutionX { get; set; } - - /// <summary> - /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images. - /// </summary> - [FieldOrder(14)] public uint ResolutionY { get; set; } - - /// <summary> - /// Gets the file offsets of ImageHeader records describing the larger preview images. - /// </summary> - [FieldOrder(15)] public uint PreviewLargeOffsetAddress { get; set; } - - /// <summary> - /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer. - /// </summary> - [FieldOrder(16)] public uint LayersDefinitionOffsetAddress { get; set; } - - /// <summary> - /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files. - /// </summary> - [FieldOrder(17)] public uint LayerCount { get; set; } - - /// <summary> - /// Gets the file offsets of ImageHeader records describing the smaller preview images. - /// </summary> - [FieldOrder(18)] public uint PreviewSmallOffsetAddress { get; set; } - - /// <summary> - /// Gets the estimated duration of print, in seconds. - /// </summary> - [FieldOrder(19)] public uint PrintTime { get; set; } - - /// <summary> - /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose. - /// </summary> - [FieldOrder(20)] public uint ProjectorType { get; set; } - - /// <summary> - /// Gets the print parameters table offset - /// </summary> - [FieldOrder(21)] public uint PrintParametersOffsetAddress { get; set; } - - /// <summary> - /// Gets the print parameters table size in bytes. - /// </summary> - [FieldOrder(22)] public uint PrintParametersSize { get; set; } - - /// <summary> - /// Gets the number of times each layer image is repeated in the file. - /// This is used to implement antialiasing in cbddlp files. When greater than 1, - /// the layer table will actually contain layer_table_count * level_set_count entries. - /// See the section on antialiasing for details. - /// </summary> - [FieldOrder(23)] public uint AntiAliasLevel { get; set; } = 1; - - /// <summary> - /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively. - /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off. - /// </summary> - [FieldOrder(24)] public ushort LightPWM { get; set; } = 255; - - /// <summary> - /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively. - /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off. - /// </summary> - [FieldOrder(25)] public ushort BottomLightPWM { get; set; } = 255; - - /// <summary> - /// Gets the key used to encrypt layer data, or 0 if encryption is not used. - /// </summary> - [FieldOrder(26)] public uint EncryptionKey { get; set; } - - /// <summary> - /// Gets the slicer tablet offset - /// </summary> - [FieldOrder(27)] public uint SlicerOffset { get; set; } - - /// <summary> - /// Gets the slicer table size in bytes - /// </summary> - [FieldOrder(28)] public uint SlicerSize { get; set; } - - public override string ToString() - { - return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(LayerOffTime)}: {LayerOffTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(PrintParametersOffsetAddress)}: {PrintParametersOffsetAddress}, {nameof(PrintParametersSize)}: {PrintParametersSize}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(SlicerOffset)}: {SlicerOffset}, {nameof(SlicerSize)}: {SlicerSize}"; - } - } - #endregion - - #region PrintParameters - public class PrintParameters - { - /// <summary> - /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters. - /// </summary> - [FieldOrder(0)] public float BottomLiftHeight { get; set; } = 5; - - /// <summary> - /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute. - /// </summary> - [FieldOrder(1)] public float BottomLiftSpeed { get; set; } = 300; - - /// <summary> - /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters. - /// </summary> - [FieldOrder(2)] public float LiftHeight { get; set; } = 5; - - /// <summary> - /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute. - /// </summary> - [FieldOrder(3)] public float LiftSpeed { get; set; } = 300; - - /// <summary> - /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute. - /// </summary> - [FieldOrder(4)] public float RetractSpeed { get; set; } = 300; - - /// <summary> - /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model. - /// </summary> - [FieldOrder(5)] public float VolumeMl { get; set; } - - /// <summary> - /// Gets the estimated grams, derived from volume using configured factors for density. - /// </summary> - [FieldOrder(6)] public float WeightG { get; set; } - - /// <summary> - /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost. - /// </summary> - [FieldOrder(7)] public float CostDollars { get; set; } - - /// <summary> - /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(8)] public float BottomLightOffDelay { get; set; } = 1; - - /// <summary> - /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(9)] public float LightOffDelay { get; set; } = 1; - - /// <summary> - /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(10)] public uint BottomLayerCount { get; set; } = 10; - [FieldOrder(11)] public uint Padding1 { get; set; } - [FieldOrder(12)] public uint Padding2 { get; set; } - [FieldOrder(13)] public uint Padding3 { get; set; } - [FieldOrder(14)] public uint Padding4 { get; set; } - - public override string ToString() - { - return $"{nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}"; - } - } - #endregion - - #region SlicerInfo - - public class SlicerInfo - { - [FieldOrder(0)] public uint Padding1 { get; set; } - [FieldOrder(1)] public uint Padding2 { get; set; } - [FieldOrder(2)] public uint Padding3 { get; set; } - [FieldOrder(3)] public uint Padding4 { get; set; } - [FieldOrder(4)] public uint Padding5 { get; set; } - [FieldOrder(5)] public uint Padding6 { get; set; } - [FieldOrder(6)] public uint Padding7 { get; set; } - - /// <summary> - /// Gets the machine name offset to a string naming the machine type, and its length in bytes. - /// </summary> - [FieldOrder(7)] public uint MachineNameAddress { get; set; } - - /// <summary> - /// Gets the machine size in bytes - /// </summary> - [FieldOrder(8)] public uint MachineNameSize { get; set; } - - /// <summary> - /// Gets the parameter used to control encryption. - /// Not totally understood. 0 for cbddlp files, 0xF for ctb files. - /// </summary> - [FieldOrder(9)] public uint EncryptionMode { get; set; } = 8; - - /// <summary> - /// Gets a number that increments with time or number of models sliced, or both. Zeroing it in output seems to have no effect. Possibly a user tracking bug. - /// </summary> - [FieldOrder(10)] public uint MysteriousId { get; set; } - - /// <summary> - /// Gets the user-selected antialiasing level. For cbddlp files this will match the level_set_count. For ctb files, this number is essentially arbitrary. - /// </summary> - [FieldOrder(11)] public uint AntiAliasLevel { get; set; } = 1; - - /// <summary> - /// Gets a version of software that generated this file, encoded with major, minor, and patch release in bytes starting from the MSB down. - /// (No provision is made to name the software being used, so this assumes that only one software package can generate the files. - /// Probably best to hardcode it at 0x01060300.) - /// </summary> - [FieldOrder(12)] public uint SoftwareVersion { get; set; } = 0x01060300; - [FieldOrder(13)] public uint Unknown1 { get; set; } - [FieldOrder(14)] public uint Padding8 { get; set; } - [FieldOrder(15)] public uint Padding9 { get; set; } - [FieldOrder(16)] public uint Padding10 { get; set; } - [FieldOrder(17)] public uint Padding11 { get; set; } - [FieldOrder(18)] public uint Padding12 { get; set; } - - /// <summary> - /// Gets the machine name. string is not nul-terminated. - /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only. - /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine. - /// </summary> - [FieldOrder(19)] [FieldLength(nameof(MachineNameSize))] - public string MachineName { get; set; } - - public override string ToString() - { - return $"{nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Unknown1)}: {Unknown1}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(Padding11)}: {Padding11}, {nameof(Padding12)}: {Padding12}, {nameof(MachineName)}: {MachineName}"; - } - } - - #endregion - - #region Preview - /// <summary> - /// The files contain two preview images. - /// These are shown on the printer display when choosing which file to print, sparing the poor printer from needing to render a 3D image from scratch. - /// </summary> - public class Preview - { - /// <summary> - /// Gets the X dimension of the preview image, in pixels. - /// </summary> - [FieldOrder(0)] public uint ResolutionX { get; set; } - - /// <summary> - /// Gets the Y dimension of the preview image, in pixels. - /// </summary> - [FieldOrder(1)] public uint ResolutionY { get; set; } - - /// <summary> - /// Gets the image offset of the encoded data blob. - /// </summary> - [FieldOrder(2)] public uint ImageOffset { get; set; } - - /// <summary> - /// Gets the image length in bytes. - /// </summary> - [FieldOrder(3)] public uint ImageLength { get; set; } - - [FieldOrder(4)] public uint Unknown1 { get; set; } - [FieldOrder(5)] public uint Unknown2 { get; set; } - [FieldOrder(6)] public uint Unknown3 { get; set; } - [FieldOrder(7)] public uint Unknown4 { get; set; } - - public Image<Rgba32> Decode(byte[] rawImageData) - { - var image = new Image<Rgba32>((int) ResolutionX, (int) ResolutionY); - image.TryGetSinglePixelSpan(out var span); - - int pixel = 0; - for (int n = 0; n < rawImageData.Length; n++) - { - uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8)); - //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3); - byte red = (byte)(((dot >> 11) & 0x1F) << 3); - byte green = (byte)(((dot >> 6) & 0x1F) << 3); - byte blue = (byte)((dot & 0x1F) << 3); - int repeat = 1; - if ((dot & 0x0020) == 0x0020) - { - repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8); - } - - for (int j = 0; j < repeat; j++) - { - span[pixel++] = new Rgba32(red, green, blue); - } - } - - return image; - } - - public override string ToString() - { - return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; - } - - public byte[] Encode(Image<Rgba32> image) - { - List<byte> rawData = new List<byte>(); - ushort color15 = 0; - uint rep = 0; - - void RleRGB15() - { - switch (rep) - { - case 0: - return; - case 1: - rawData.Add((byte)(color15 & ~REPEATRGB15MASK)); - rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8)); - break; - case 2: - for (int i = 0; i < 2; i++) - { - rawData.Add((byte)(color15 & ~REPEATRGB15MASK)); - rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8)); - } - - break; - default: - rawData.Add((byte)(color15 | REPEATRGB15MASK)); - rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8)); - rawData.Add((byte)((rep - 1) | 0x3000)); - rawData.Add((byte)(((rep - 1) | 0x3000) >> 8)); - break; - } - } - - for (int y = 0; y < image.Height; y++) - { - Span<Rgba32> pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var ncolor15 = - (pixelRowSpan[x].B >> 3) - | ((pixelRowSpan[x].G >> 2) << 5) - | ((pixelRowSpan[x].R >> 3) << 11); - - if (ncolor15 == color15) - { - rep++; - if (rep == RLE16EncodingLimit) - { - RleRGB15(); - rep = 0; - } - } - else - { - RleRGB15(); - color15 = (ushort)ncolor15; - rep = 1; - } - } - } - - RleRGB15(); - - ImageLength = (uint) rawData.Count; - - return rawData.ToArray(); - } - } - - #endregion - - #region Layer - public class LayerData - { - /// <summary> - /// Gets the build platform Z position for this layer, measured in millimeters. - /// </summary> - [FieldOrder(0)] public float LayerPositionZ { get; set; } - - /// <summary> - /// Gets the exposure time for this layer, in seconds. - /// </summary> - [FieldOrder(1)] public float LayerExposure { get; set; } - - /// <summary> - /// Gets how long to keep the light off after exposing this layer, in seconds. - /// </summary> - [FieldOrder(2)] public float LayerOffTimeSeconds { get; set; } - - /// <summary> - /// Gets the layer image offset to encoded layer data, and its length in bytes. - /// </summary> - [FieldOrder(3)] public uint DataAddress { get; set; } - - /// <summary> - /// Gets the layer image length in bytes. - /// </summary> - [FieldOrder(4)] public uint DataSize { get; set; } - [FieldOrder(5)] public uint Unknown1 { get; set; } - [FieldOrder(6)] public uint Unknown2 { get; set; } - [FieldOrder(7)] public uint Unknown3 { get; set; } - [FieldOrder(8)] public uint Unknown4 { get; set; } - - [Ignore] public byte[] EncodedRle { get; set; } - [Ignore] public ChituboxFile Parent { get; set; } - - public LayerData() - { - } - - public LayerData(ChituboxFile parent, uint layerIndex) - { - Parent = parent; - LayerPositionZ = parent.GetHeightFromLayer(layerIndex); - - LayerOffTimeSeconds = layerIndex < parent.HeaderSettings.BottomLayersCount - ? parent.PrintParametersSettings.BottomLightOffDelay - : parent.PrintParametersSettings.LightOffDelay; - - LayerExposure = layerIndex < parent.HeaderSettings.BottomLayersCount - ? parent.HeaderSettings.BottomExposureSeconds - : parent.HeaderSettings.LayerExposureSeconds; - } - - public Image<L8> Decode(uint layerIndex, bool consumeData = true) - { - var image = Parent.IsCbtFile ? DecodeCbtImage(layerIndex) : DecodeCbddlpImage(Parent, layerIndex); - - if (consumeData) - EncodedRle = null; - - return image; - } - - public static Image<L8> DecodeCbddlpImage(ChituboxFile parent, uint layerIndex) - { - Image<L8> image = new Image<L8>((int)parent.HeaderSettings.ResolutionX, (int)parent.HeaderSettings.ResolutionY); - image.TryGetSinglePixelSpan(out var span); - - for (byte bit = 0; bit < parent.AntiAliasing; bit++) - { - var layer = parent.LayersDefinitions[bit, layerIndex]; - - int n = 0; - for (int index = 0; index < layer.DataSize; index++) - { - // Lower 7 bits is the repeat count for the bit (0..127) - int reps = layer.EncodedRle[index] & 0x7f; - - // We only need to set the non-zero pixels - // High bit is on for white, off for black - if ((layer.EncodedRle[index] & 0x80) != 0) - { - for (int i = 0; i < reps; i++) - { - span[n + i].PackedValue++; - } - } - - n += reps; - - if (n == span.Length) - { - break; - } - - if (n > span.Length) - { - throw new FileLoadException("Error image ran off the end"); - } - } - } - - for (int i = 0; i < span.Length; i++) - { - int newC = span[i].PackedValue * (256 / parent.AntiAliasing); - - if (newC > 0) - { - newC--; - } - - span[i].PackedValue = (byte) newC; - - - } - - return image; - } - - private Image<L8> DecodeCbtImage(uint layerIndex) - { - Image<L8> image = new Image<L8>((int)Parent.HeaderSettings.ResolutionX, (int)Parent.HeaderSettings.ResolutionY); - image.TryGetSinglePixelSpan(out var span); - - - if (Parent.HeaderSettings.EncryptionKey > 0) - { - KeyRing kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex); - EncodedRle = kr.Read(EncodedRle); - } - - int pixel = 0; - for (var n = 0; n < EncodedRle.Length; n++) - { - byte code = EncodedRle[n]; - uint stride = 1; - - if ((code & 0x80) == 0x80) // It's a run - { - code &= 0x7f; // Get the run length - n++; - - var slen = EncodedRle[n]; - - if ((slen & 0x80) == 0) - { - stride = slen; - } - else if ((slen & 0xc0) == 0x80) - { - stride = (uint)(((slen & 0x3f) << 8) + EncodedRle[n + 1]); - n++; - } - else if ((slen & 0xe0) == 0xc0) - { - stride = (uint)(((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2]); - n += 2; - } - else if ((slen & 0xf0) == 0xe0) - { - stride = (uint)(((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3]); - - n += 3; - } - else - { - throw new FileLoadException("Corrupted RLE data"); - } - } - - // Bit extend from 7-bit to 8-bit greymap - if (code != 0) - { - code = (byte)((code << 1) | 1); - } - - if (stride == 0) continue; // Nothing to do - - if (code == 0) // Ignore blacks, spare cycles - { - pixel += (int)stride; - continue; - } - - while (stride-- > 0) - { - span[pixel].PackedValue = code; - pixel++; - } - } - - return image; - } - - public byte[] Encode(Image<L8> image, byte aaIndex, uint layerIndex) - { - return Parent.IsCbtFile ? EncodeCbtImage(image, layerIndex) : EncodeCbddlpImage(image, aaIndex); - } - - public byte[] EncodeCbddlpImage(Image<L8> image, byte bit) - { - List<byte> rawData = new List<byte>(); - - bool obit = false; - int rep = 0; - - //ngrey:= uint16(r | g | b) - // thresholds: - // aa 1: 127 - // aa 2: 255 127 - // aa 4: 255 191 127 63 - // aa 8: 255 223 191 159 127 95 63 31 - byte threshold = (byte)(256 / Parent.AntiAliasing * bit - 1); - - void AddRep() - { - if (rep <= 0) return; - - byte by = (byte)rep; - - if (obit) - { - by |= 0x80; - //bitsOn += uint(rep) - } - - rawData.Add(by); - } - - for (int y = 0; y < image.Height; y++) - { - Span<L8> pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var nbit = pixelRowSpan[x].PackedValue >= threshold; - - if (nbit == obit) - { - rep++; - - if (rep == RLE8EncodingLimit) - { - AddRep(); - rep = 0; - } - } - else - { - AddRep(); - obit = nbit; - rep = 1; - } - } - } - - // Collect stragglers - AddRep(); - - EncodedRle = rawData.ToArray(); - DataSize = (uint) EncodedRle.Length; - - return EncodedRle; - } - - private byte[] EncodeCbtImage(Image<L8> image, uint layerIndex) - { - List<byte> rawData = new List<byte>(); - byte color = byte.MaxValue >> 1; - uint stride = 0; - - void AddRep() - { - if (stride == 0) - { - return; - } - - if (stride > 1) - { - color |= 0x80; - } - rawData.Add(color); - - if (stride <= 1) - { - // no run needed - return; - } - - if (stride <= 0x7f) - { - rawData.Add((byte)stride); - return; - } - - if (stride <= 0x3fff) - { - rawData.Add((byte)((stride >> 8) | 0x80)); - rawData.Add((byte)stride); - return; - } - - if (stride <= 0x1fffff) - { - rawData.Add((byte)((stride >> 16) | 0xc0)); - rawData.Add((byte)(stride >> 8)); - rawData.Add((byte)stride); - return; - } - - if (stride <= 0xfffffff) - { - rawData.Add((byte)((stride >> 24) | 0xe0)); - rawData.Add((byte)(stride >> 16)); - rawData.Add((byte)(stride >> 8)); - rawData.Add((byte)stride); - } - - } - - - for (int y = 0; y < image.Height; y++) - { - var pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var grey7 = (byte)(pixelRowSpan[x].PackedValue >> 1); - - if (grey7 == color) - { - stride++; - } - else - { - AddRep(); - color = grey7; - stride = 1; - } - } - } - - AddRep(); - - if (Parent.HeaderSettings.EncryptionKey > 0) - { - KeyRing kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex); - EncodedRle = kr.Read(rawData.ToArray()); - } - else - { - EncodedRle = rawData.ToArray(); - } - - DataSize = (uint)EncodedRle.Length; - - return EncodedRle; - } - - public override string ToString() - { - return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LayerOffTimeSeconds)}: {LayerOffTimeSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; - } - - - } - #endregion - - #region KeyRing - - public class KeyRing - { - public uint Init { get; } - public uint Key { get; private set; } - public uint Index { get; private set; } - - public KeyRing(uint seed, uint layerIndex) - { - Init = seed * 0x2d83cdac + 0xd8a83423; - Key = (layerIndex * 0x1e1530cd + 0xec3d47cd) * Init; - } - - public byte Next() - { - byte k = (byte)(Key >> (int)(8 * Index)); - - Index++; - - if ((Index & 3) == 0) - { - Key += Init; - Index = 0; - } - - return k; - } - - public List<byte> Read(List<byte> input) - { - List<byte> data = new List<byte>(input.Count); - data.AddRange(input.Select(t => (byte) (t ^ Next()))); - - return data; - } - - public byte[] Read(byte[] input) - { - byte[] data = new byte[input.Length]; - for (int i = 0; i < input.Length; i++) - { - data[i] = (byte)(input[i]^Next()); - } - return data; - } - } - - #endregion - - #endregion - - #region Properties - - public Header HeaderSettings { get; protected internal set; } = new Header(); - public PrintParameters PrintParametersSettings { get; protected internal set; } = new PrintParameters(); - - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new SlicerInfo(); - - public Preview[] Previews { get; protected internal set; } - - public LayerData[,] LayersDefinitions { get; private set; } - - public Dictionary<string, LayerData> LayersHash { get; } = new Dictionary<string, LayerData>(); - - public override FileFormatType FileType => FileFormatType.Binary; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("cbddlp", "Chitubox DLP Files"), - new FileExtension("ctb", "Chitubox CTB Files"), - new FileExtension("photon", "Chitubox Photon Files"), - }; - - public override Type[] ConvertToFormats { get; } = - { - typeof(ChituboxZipFile), - typeof(PWSFile), - typeof(PHZFile), - typeof(ZCodexFile), - }; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = - { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - PrintParameterModifier.BottomLayerOffTime, - PrintParameterModifier.LayerOffTime, - PrintParameterModifier.BottomLiftHeight, - PrintParameterModifier.BottomLiftSpeed, - PrintParameterModifier.LiftHeight, - PrintParameterModifier.LiftSpeed, - PrintParameterModifier.RetractSpeed, - - PrintParameterModifier.BottomLightPWM, - PrintParameterModifier.LightPWM, - }; - - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)}; - - public override uint ResolutionX => HeaderSettings.ResolutionX; - - public override uint ResolutionY => HeaderSettings.ResolutionY; - public override byte AntiAliasing => (byte) (IsCbtFile ? SlicerInfoSettings.AntiAliasLevel : HeaderSettings.AntiAliasLevel); - - public override float LayerHeight => HeaderSettings.LayerHeightMilimeter; - - public override ushort InitialLayerCount => (ushort)HeaderSettings.BottomLayersCount; - - public override float InitialExposureTime => HeaderSettings.BottomExposureSeconds; - - public override float LayerExposureTime => HeaderSettings.LayerExposureSeconds; - public override float LiftHeight => PrintParametersSettings.LiftHeight; - public override float LiftSpeed => PrintParametersSettings.LiftSpeed; - public override float RetractSpeed => PrintParametersSettings.RetractSpeed; - - public override float PrintTime => HeaderSettings.PrintTime; - - public override float UsedMaterial => (float) Math.Round(PrintParametersSettings.VolumeMl, 2); - - public override float MaterialCost => (float) Math.Round(PrintParametersSettings.CostDollars, 2); - - public override string MaterialName => "Unknown"; - public override string MachineName => SlicerInfoSettings.MachineName; - - public override object[] Configs => new[] { (object)HeaderSettings, PrintParametersSettings, SlicerInfoSettings }; - - public bool IsCbddlpFile => HeaderSettings.Magic == MAGIC_CBDDLP; - public bool IsCbtFile => HeaderSettings.Magic == MAGIC_CBT; - #endregion - - #region Constructors - public ChituboxFile() - { - Previews = new Preview[ThumbnailsCount]; - } - #endregion - - #region Methods - public override void Clear() - { - base.Clear(); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - Previews[i] = new Preview(); - } - - LayersDefinitions = null; - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - LayersHash.Clear(); - - HeaderSettings.Magic = fileFullPath.EndsWith(".ctb") ? MAGIC_CBT : MAGIC_CBDDLP; - HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings); - - - if (IsCbtFile) - { - SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel; - HeaderSettings.AntiAliasLevel = 1; - PrintParametersSettings.Padding4 = 0x1234; - //SlicerInfoSettings.EncryptionMode = 0xf; - SlicerInfoSettings.EncryptionMode = 7; - SlicerInfoSettings.MysteriousId = 0x12345678; - SlicerInfoSettings.Unknown1 = 0x200; - - if (HeaderSettings.EncryptionKey == 0) - { - Random rnd = new Random(); - HeaderSettings.EncryptionKey = (uint)rnd.Next(byte.MaxValue, int.MaxValue); - } - } - - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - LayersDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount]; - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); - - - for (byte i = 0; i < ThumbnailsCount; i++) - { - var image = Thumbnails[i]; - - Preview preview = new Preview - { - ResolutionX = (uint)image.Width, - ResolutionY = (uint)image.Height, - }; - - var previewBytes = preview.Encode(image); - - if (previewBytes.Length == 0) continue; - - if (i == (byte) FileThumbnailSize.Small) - { - HeaderSettings.PreviewSmallOffsetAddress = currentOffset; - } - else - { - HeaderSettings.PreviewLargeOffsetAddress = currentOffset; - } - - - currentOffset += (uint) Helpers.Serializer.SizeOf(preview); - preview.ImageOffset = currentOffset; - - Helpers.SerializeWriteFileStream(outputFile, preview); - currentOffset += outputFile.WriteBytes(previewBytes); - } - - - if (HeaderSettings.Version == 2) - { - HeaderSettings.PrintParametersOffsetAddress = currentOffset; - - currentOffset += Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings); - - HeaderSettings.SlicerOffset = currentOffset; - HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - SlicerInfoSettings.MachineNameSize; - - SlicerInfoSettings.MachineNameAddress = currentOffset + HeaderSettings.SlicerSize; - - - currentOffset += Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); - } - - HeaderSettings.LayersDefinitionOffsetAddress = currentOffset; - uint layerDataCurrentOffset = currentOffset + (uint)Helpers.Serializer.SizeOf(new LayerData()) * HeaderSettings.LayerCount * HeaderSettings.AntiAliasLevel; - - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) - { - Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => - { - LayerData layerData = new LayerData(this, (uint) layerIndex); - var image = this[layerIndex].Image; - layerData.Encode(image, aaIndex, (uint) layerIndex); - LayersDefinitions[aaIndex, layerIndex] = layerData; - }); - - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - var layerData = LayersDefinitions[aaIndex, layerIndex]; - LayerData layerDataHash = null; - - if (!IsCbtFile && HeaderSettings.EncryptionKey == 0) - { - string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle); - if (LayersHash.TryGetValue(hash, out layerDataHash)) - { - layerData.DataAddress = layerDataHash.DataAddress; - layerData.DataSize = layerDataHash.DataSize; - } - else - { - LayersHash.Add(hash, layerData); - } - } - - if (ReferenceEquals(layerDataHash, null)) - { - layerData.DataAddress = layerDataCurrentOffset; - - outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); - layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle); - } - - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layerData); - } - } - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - outputFile.Close(); - outputFile.Dispose(); - - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(Previews[0]); - Debug.WriteLine(Previews[1]); - Debug.WriteLine(PrintParametersSettings); - Debug.WriteLine(SlicerInfoSettings); - Debug.WriteLine("-End-"); - } - } - - - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); - - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Magic != MAGIC_CBDDLP && HeaderSettings.Magic != MAGIC_CBT) - { - throw new FileLoadException("Not a valid CBDDLP nor CTB nor Photon file!", fileFullPath); - } - - if (HeaderSettings.Version == 1 || HeaderSettings.AntiAliasLevel == 0) - { - HeaderSettings.AntiAliasLevel = 1; - } - - FileFullPath = fileFullPath; - - - - Debug.Write("Header -> "); - Debug.WriteLine(HeaderSettings); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - uint offsetAddress = i == 0 ? HeaderSettings.PreviewSmallOffsetAddress : HeaderSettings.PreviewLargeOffsetAddress; - if (offsetAddress == 0) continue; - - inputFile.Seek(offsetAddress, SeekOrigin.Begin); - Previews[i] = Helpers.Deserialize<Preview>(inputFile); - - Debug.Write($"Preview {i} -> "); - Debug.WriteLine(Previews[i]); - - inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); - byte[] rawImageData = new byte[Previews[i].ImageLength]; - inputFile.Read(rawImageData, 0, (int)Previews[i].ImageLength); - - Thumbnails[i] = Previews[i].Decode(rawImageData); - } - - //if (HeaderSettings.Version == 2) - //{ - if (HeaderSettings.PrintParametersOffsetAddress > 0) - { - inputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin); - PrintParametersSettings = Helpers.Deserialize<PrintParameters>(inputFile); - Debug.Write("Print Parameters -> "); - Debug.WriteLine(PrintParametersSettings); - - - } - - if (HeaderSettings.SlicerOffset > 0) - { - inputFile.Seek(HeaderSettings.SlicerOffset, SeekOrigin.Begin); - SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); - Debug.Write("Slicer Info -> "); - Debug.WriteLine(SlicerInfoSettings); - } - - /*InputFile.BaseStream.Seek(MachineInfoSettings.MachineNameAddress, SeekOrigin.Begin); - byte[] bytes = InputFile.ReadBytes((int)MachineInfoSettings.MachineNameSize); - MachineName = System.Text.Encoding.UTF8.GetString(bytes); - Debug.WriteLine($"{nameof(MachineName)}: {MachineName}");*/ - //} - - LayersDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount]; - - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - - - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) - { - Debug.WriteLine($"-Image GROUP {aaIndex}-"); - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - inputFile.Seek(layerOffset, SeekOrigin.Begin); - LayerData layerData = Helpers.Deserialize<LayerData>(inputFile); - layerData.Parent = this; - LayersDefinitions[aaIndex, layerIndex] = layerData; - - layerOffset += (uint)Helpers.Serializer.SizeOf(layerData); - Debug.Write($"LAYER {layerIndex} -> "); - Debug.WriteLine(layerData); - - layerData.EncodedRle = new byte[layerData.DataSize]; - inputFile.Seek(layerData.DataAddress, SeekOrigin.Begin); - inputFile.Read(layerData.EncodedRle, 0, (int)layerData.DataSize); - } - } - - LayerManager = new LayerManager(HeaderSettings.LayerCount); - - Parallel.For(0, LayerCount, layerIndex => - { - var image = LayersDefinitions[0, layerIndex].Decode((uint) layerIndex); - this[layerIndex] = new Layer((uint)layerIndex, image); - }); - } - - public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - var baseValue = base.GetValueFromPrintParameterModifier(modifier); - if (!ReferenceEquals(baseValue, null)) return baseValue; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) return PrintParametersSettings.BottomLightOffDelay; - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return PrintParametersSettings.LightOffDelay; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) return PrintParametersSettings.BottomLiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) return PrintParametersSettings.BottomLiftSpeed; - /*if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) return PrintParametersSettings.LiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return PrintParametersSettings.LiftingSpeed; - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return PrintParametersSettings.RetractSpeed;*/ - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return HeaderSettings.BottomLightPWM; - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return HeaderSettings.LightPWM; - - - - return null; - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - void UpdateLayers() - { - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) - { - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - // Bottom : others - LayersDefinitions[aaIndex, layerIndex].LayerExposure = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomExposureSeconds : HeaderSettings.LayerExposureSeconds; - LayersDefinitions[aaIndex, layerIndex].LayerOffTimeSeconds = layerIndex < HeaderSettings.BottomLayersCount ? PrintParametersSettings.BottomLightOffDelay : PrintParametersSettings.LightOffDelay; - } - } - } - - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - HeaderSettings.BottomLayersCount = - PrintParametersSettings.BottomLayerCount = value.Convert<uint>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - HeaderSettings.BottomExposureSeconds = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - HeaderSettings.LayerExposureSeconds = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) - { - PrintParametersSettings.BottomLightOffDelay = value.Convert<float>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) - { - HeaderSettings.LayerOffTime = - PrintParametersSettings.LightOffDelay = value.Convert<float>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) - { - PrintParametersSettings.BottomLiftHeight = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) - { - PrintParametersSettings.BottomLiftSpeed = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - PrintParametersSettings.LiftHeight = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - PrintParametersSettings.LiftSpeed = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - PrintParametersSettings.RetractSpeed = value.Convert<float>(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) - { - HeaderSettings.BottomLightPWM = value.Convert<ushort>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) - { - HeaderSettings.LightPWM = value.Convert<ushort>(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (LayerManager.IsModified) - { - if (!string.IsNullOrEmpty(filePath)) - { - FileFullPath = filePath; - } - Encode(FileFullPath); - return; - } - - - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - if (HeaderSettings.Version == 2 && HeaderSettings.PrintParametersOffsetAddress > 0) - { - outputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings); - Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); - } - - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) - { - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - outputFile.Seek(layerOffset, SeekOrigin.Begin); - layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[aaIndex, layerIndex]); - } - } - outputFile.Close(); - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - if (to == typeof(ChituboxZipFile)) - { - ChituboxZipFile file = new ChituboxZipFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Filename = Path.GetFileName(FileFullPath), - - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - MachineX = HeaderSettings.BedSizeX, - MachineY = HeaderSettings.BedSizeY, - MachineZ = HeaderSettings.BedSizeZ, - MachineType = MachineName, - ProjectType = HeaderSettings.ProjectorType == 0 ? "Normal" : "LCD_mirror", - - Resin = MaterialName, - Price = MaterialCost, - Weight = PrintParametersSettings.WeightG, - Volume = UsedMaterial, - Mirror = (byte) (HeaderSettings.ProjectorType == 0 ? 0 : 1), - - - BottomLayerLiftHeight = PrintParametersSettings.BottomLiftHeight, - LayerLiftHeight = PrintParametersSettings.LiftHeight, - BottomLayerLiftSpeed = PrintParametersSettings.BottomLiftSpeed, - LayerLiftSpeed = PrintParametersSettings.LiftSpeed, - RetractSpeed = PrintParametersSettings.RetractSpeed, - BottomLayCount = InitialLayerCount, - BottomLayerCount = InitialLayerCount, - BottomLightOffTime = PrintParametersSettings.BottomLightOffDelay, - LayerLightOffTime = PrintParametersSettings.LightOffDelay, - BottomLayExposureTime = InitialExposureTime, - BottomLayerExposureTime = InitialExposureTime, - LayerExposureTime = LayerExposureTime, - LayerHeight = LayerHeight, - LayerCount = LayerCount, - AntiAliasing = ValidateAntiAliasingLevel(), - BottomLightPWM = (byte) HeaderSettings.BottomLightPWM, - LayerLightPWM = (byte) HeaderSettings.LightPWM, - - EstimatedPrintTime = PrintTime - }, - }; - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(PWSFile)) - { - PWSFile file = new PWSFile - { - LayerManager = LayerManager, - HeaderSettings = - { - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - LayerHeight = LayerHeight, - LayerExposureTime = LayerExposureTime, - LiftHeight = LiftHeight, - LiftSpeed = LiftSpeed / 60, - RetractSpeed = RetractSpeed / 60, - LayerOffTime = HeaderSettings.LayerOffTime, - BottomLayersCount = InitialLayerCount, - BottomExposureSeconds = InitialExposureTime, - Price = MaterialCost, - Volume = UsedMaterial, - Weight = PrintParametersSettings.WeightG, - AntiAliasing = ValidateAntiAliasingLevel() - } - }; - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(PHZFile)) - { - PHZFile file = new PHZFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Version = 2, - BedSizeX = HeaderSettings.BedSizeX, - BedSizeY = HeaderSettings.BedSizeY, - BedSizeZ = HeaderSettings.BedSizeZ, - OverallHeightMilimeter = TotalHeight, - BottomExposureSeconds = InitialExposureTime, - BottomLayersCount = InitialLayerCount, - BottomLightPWM = HeaderSettings.BottomLightPWM, - LayerCount = LayerCount, - LayerExposureSeconds = LayerExposureTime, - LayerHeightMilimeter = LayerHeight, - LayerOffTime = HeaderSettings.LayerOffTime, - LightPWM = HeaderSettings.LightPWM, - PrintTime = HeaderSettings.PrintTime, - ProjectorType = HeaderSettings.ProjectorType, - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - BottomLayerCount = InitialLayerCount, - BottomLiftHeight = PrintParametersSettings.BottomLiftHeight, - BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed, - BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay, - CostDollars = MaterialCost, - LiftHeight = PrintParametersSettings.LiftHeight, - LiftSpeed = PrintParametersSettings.LiftSpeed, - RetractSpeed = PrintParametersSettings.RetractSpeed, - VolumeMl = UsedMaterial, - AntiAliasLevelInfo = ValidateAntiAliasingLevel(), - WeightG = PrintParametersSettings.WeightG, - MachineName = MachineName, - MachineNameSize = (uint)MachineName.Length - } - }; - - - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ZCodexFile)) - { - TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime); - ZCodexFile file = new ZCodexFile - { - ResinMetadataSettings = new ZCodexFile.ResinMetadata - { - MaterialId = 2, - Material = MaterialName, - AdditionalSupportLayerTime = 0, - BottomLayersNumber = InitialLayerCount, - BottomLayersTime = (uint)(InitialExposureTime * 1000), - LayerTime = (uint)(LayerExposureTime * 1000), - DisableSettingsChanges = false, - LayerThickness = LayerHeight, - PrintTime = (uint)PrintTime, - TotalLayersCount = LayerCount, - TotalMaterialVolumeUsed = UsedMaterial, - TotalMaterialWeightUsed = UsedMaterial, - }, - UserSettings = new ZCodexFile.UserSettingsdata - { - Printer = MachineName, - BottomLayersCount = InitialLayerCount, - PrintTime = $"{ts.Hours}h {ts.Minutes}m", - LayerExposureTime = (uint)(LayerExposureTime * 1000), - BottomLayerExposureTime = (uint)(InitialExposureTime * 1000), - MaterialId = 2, - LayerThickness = $"{LayerHeight} mm", - AntiAliasing = (byte)(ValidateAntiAliasingLevel() > 1 ? 1 : 0), - CrossSupportEnabled = 1, - ExposureOffTime = (uint) HeaderSettings.LayerOffTime, - HollowEnabled = 0, - HollowThickness = 0, - InfillDensity = 0, - IsAdvanced = 0, - MaterialType = MaterialName, - MaterialVolume = UsedMaterial, - MaxLayer = LayerCount - 1, - ModelLiftEnabled = 0, - ModelLiftHeight = 0, - RaftEnabled = 0, - RaftHeight = 0, - RaftOffset = 0, - SupportAdditionalExposureEnabled = 0, - SupportAdditionalExposureTime = 0, - XCorrection = 0, - YCorrection = 0, - ZLiftDistance = PrintParametersSettings.LiftHeight, - ZLiftFeedRate = PrintParametersSettings.LiftSpeed, - ZLiftRetractRate = PrintParametersSettings.RetractSpeed, - }, - ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata - { - PrintTime = (uint)PrintTime, - PrinterName = MachineName, - Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData> - { - new ZCodexFile.ZCodeMetadata.MaterialsData - { - Name = MaterialName, - ExtruderType = "MAIN", - Id = 0, - Usage = 0, - Temperature = 0 - } - }, - }, - LayerManager = LayerManager - }; - - float usedMaterial = UsedMaterial / LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData - { - Layer = layerIndex, - UsedMaterialVolume = usedMaterial - }); - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - return true; - } - - return false; - } - #endregion - } -} diff --git a/UVtools.Parser/ChituboxZipFile.cs b/UVtools.Parser/ChituboxZipFile.cs deleted file mode 100644 index 2430b8c..0000000 --- a/UVtools.Parser/ChituboxZipFile.cs +++ /dev/null @@ -1,435 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.ComponentModel; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection; -using System.Text; -using SixLabors.ImageSharp; -using UVtools.Parser.Extensions; -using Size = System.Drawing.Size; - -namespace UVtools.Parser -{ - public class ChituboxZipFile : FileFormat - { - #region Constants - - public const string GCodeStart = ";START_GCODE_BEGIN{0}" + - "G21 ;Set units to be mm{0}" + - "G90 ;Absolute Positioning{0}" + - "M17 ;Enable motors{0}" + - "G28 Z0 ;Home Z{0}" + - //"G91 ;Relative Positioning{0}" + - "M106 S0 ;Light off{0}" + - ";START_GCODE_END{0}{0}"; - - public const string GCodeEnd = ";END_GCODE_BEGIN{0}" + - "M106 S0 ;Light off{0}" + - "G1 Z{1} F25 ;Raize Z{0}" + - "M18 ;Disable Motors{0}" + - ";END_GCODE_END{0}"; - - #endregion - - #region Sub Classes - - public class Header - { - // ;(****Build and Slicing Parameters****) - [DisplayName("fileName")] public string Filename { get; set; } = string.Empty; - [DisplayName("machineType")] public string MachineType { get; set; } = "Default"; - [DisplayName("estimatedPrintTime")] public float EstimatedPrintTime { get; set; } - [DisplayName("volume")] public float Volume { get; set; } - [DisplayName("resin")] public string Resin { get; set; } = "Normal"; - [DisplayName("weight")] public float Weight { get; set; } - [DisplayName("price")] public float Price { get; set; } - [DisplayName("layerHeight")] public float LayerHeight { get; set; } - [DisplayName("resolutionX")] public uint ResolutionX { get; set; } - [DisplayName("resolutionY")] public uint ResolutionY { get; set; } - [DisplayName("machineX")] public float MachineX { get; set; } - [DisplayName("machineY")] public float MachineY { get; set; } - [DisplayName("machineZ")] public float MachineZ { get; set; } - [DisplayName("projectType")] public string ProjectType { get; set; } = "Normal"; - [DisplayName("normalExposureTime")] public float LayerExposureTime { get; set; } = 7; // 35s - [DisplayName("bottomLayExposureTime")] public float BottomLayExposureTime { get; set; } = 35; // 35s - [DisplayName("bottomLayerExposureTime")] public float BottomLayerExposureTime { get; set; } = 35; // 35s - [DisplayName("normalDropSpeed")] public float RetractSpeed { get; set; } = 150; // 150 mm/m - [DisplayName("normalLayerLiftSpeed")] public float LayerLiftSpeed { get; set; } = 60; // 60 mm/m - [DisplayName("normalLayerLiftHeight")] public float LayerLiftHeight { get; set; } = 5; // 5 mm - [DisplayName("zSlowUpDistance")] public float ZSlowUpDistance { get; set; } - [DisplayName("bottomLayCount")] public ushort BottomLayCount { get; set; } = 4; - [DisplayName("bottomLayerCount")] public ushort BottomLayerCount { get; set; } = 4; - [DisplayName("mirror")] public byte Mirror { get; set; } // 0/1 - [DisplayName("totalLayer")] public uint LayerCount { get; set; } - [DisplayName("bottomLayerLiftHeight")] public float BottomLayerLiftHeight { get; set; } = 5; - [DisplayName("bottomLayerLiftSpeed")] public float BottomLayerLiftSpeed { get; set; } = 60; - [DisplayName("bottomLightOffTime")] public float BottomLightOffTime { get; set; } - [DisplayName("lightOffTime")] public float LayerLightOffTime { get; set; } - [DisplayName("bottomPWMLight")] public byte BottomLightPWM { get; set; } = 255; - [DisplayName("PWMLight")] public byte LayerLightPWM { get; set; } = 255; - [DisplayName("antiAliasLevel")] public byte AntiAliasing { get; set; } = 1; - } - - #endregion - - #region Properties - public Header HeaderSettings { get; } = new Header(); - - public override FileFormatType FileType => FileFormatType.Archive; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("zip", "Chitubox Zip Files") - }; - - public override Type[] ConvertToFormats { get; } = null; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - PrintParameterModifier.BottomLayerOffTime, - PrintParameterModifier.LayerOffTime, - PrintParameterModifier.BottomLiftHeight, - PrintParameterModifier.BottomLiftSpeed, - PrintParameterModifier.LiftHeight, - PrintParameterModifier.LiftSpeed, - PrintParameterModifier.RetractSpeed, - - PrintParameterModifier.BottomLightPWM, - PrintParameterModifier.LightPWM, - }; - - public override byte ThumbnailsCount { get; } = 2; - - public override Size[] ThumbnailsOriginalSize { get; } = {new Size(954, 850), new Size(168, 150)}; - - public override uint ResolutionX => HeaderSettings.ResolutionX; - - public override uint ResolutionY => HeaderSettings.ResolutionY; - public override byte AntiAliasing => HeaderSettings.AntiAliasing; - - public override float LayerHeight => HeaderSettings.LayerHeight; - - public override ushort InitialLayerCount => HeaderSettings.BottomLayerCount; - - public override float InitialExposureTime => HeaderSettings.BottomLayerExposureTime; - - public override float LayerExposureTime => HeaderSettings.LayerExposureTime; - - public override float LiftHeight => HeaderSettings.LayerLiftHeight; - - public override float LiftSpeed => HeaderSettings.LayerLiftSpeed; - - public override float RetractSpeed => HeaderSettings.RetractSpeed; - - public override float PrintTime => HeaderSettings.EstimatedPrintTime; - - public override float UsedMaterial => HeaderSettings.Weight; - - public override float MaterialCost => HeaderSettings.Price; - - public override string MaterialName => HeaderSettings.Resin; - - public override string MachineName => HeaderSettings.MachineType; - - public override object[] Configs => new object[] { HeaderSettings }; - #endregion - - #region Methods - - public override void Clear() - { - base.Clear(); - GCode = null; - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Update)) - { - for(uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - Layer layer = this[layerIndex]; - outputFile.PutFileContent($"{layerIndex+1}.png", layer.RawData); - } - - if (Thumbnails.Length > 0 && !ReferenceEquals(Thumbnails[0], null)) - { - using (Stream stream = outputFile.CreateEntry("preview.png").Open()) - { - Thumbnails[0].Save(stream, Helpers.PngEncoder); - stream.Close(); - } - } - - if (Thumbnails.Length > 1 && !ReferenceEquals(Thumbnails[1], null)) - { - using (Stream stream = outputFile.CreateEntry("preview_cropping.png").Open()) - { - Thumbnails[1].Save(stream, Helpers.PngEncoder); - stream.Close(); - } - } - - UpdateGCode(); - outputFile.PutFileContent("run.gcode", GCode.ToString()); - } - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) - { - var entry = inputFile.GetEntry("run.gcode"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("run.gcode not found", fileFullPath); - } - - using (TextReader tr = new StreamReader(entry.Open())) - { - string line; - GCode = new StringBuilder(); - while ((line = tr.ReadLine()) != null) - { - GCode.AppendLine(line); - if (string.IsNullOrEmpty(line)) continue; - - if (line[0] != ';') - { - continue; - } - - var splitLine = line.Split(':'); - if (splitLine.Length < 2) continue; - - foreach (var propertyInfo in HeaderSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - if (!splitLine[0].Trim(' ', ';').Equals(displayNameAttribute.DisplayName)) continue; - Helpers.SetPropertyValue(propertyInfo, HeaderSettings, splitLine[1].Trim()); - } - } - tr.Close(); - } - - - LayerManager = new LayerManager(HeaderSettings.LayerCount); - - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - entry = inputFile.GetEntry($"{layerIndex+1}.png"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException($"Layer {layerIndex+1} not found", fileFullPath); - } - - LayerManager[layerIndex] = new Layer(layerIndex, entry.Open(), entry.Name); - } - - entry = inputFile.GetEntry("preview.png"); - if (!ReferenceEquals(entry, null)) - { - Thumbnails[0] = Image.Load(entry.Open().ToArray()); - } - - entry = inputFile.GetEntry("preview_cropping.png"); - if (!ReferenceEquals(entry, null)) - { - Thumbnails[CreatedThumbnailsCount] = Image.Load(entry.Open().ToArray()); - } - } - } - - public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - var baseValue = base.GetValueFromPrintParameterModifier(modifier); - if (!ReferenceEquals(baseValue, null)) return baseValue; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) return HeaderSettings.BottomLightOffTime; - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return HeaderSettings.LayerLightOffTime; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) return HeaderSettings.BottomLayerLiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) return HeaderSettings.BottomLayerLiftSpeed; - /*if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) return PrintParametersSettings.LiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return PrintParametersSettings.LiftingSpeed; - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return PrintParametersSettings.RetractSpeed;*/ - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return HeaderSettings.BottomLightPWM; - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return HeaderSettings.LayerLightPWM; - - - - return null; - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - HeaderSettings.BottomLayerCount = - HeaderSettings.BottomLayCount = value.Convert<ushort>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - HeaderSettings.BottomLayerExposureTime = value.Convert<float>(); - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - HeaderSettings.LayerExposureTime = value.Convert<float>(); - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) - { - HeaderSettings.BottomLightOffTime = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) - { - HeaderSettings.LayerLightOffTime = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) - { - HeaderSettings.BottomLayerLiftHeight = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) - { - HeaderSettings.LayerLiftSpeed = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - HeaderSettings.LayerLiftHeight = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - HeaderSettings.LayerLiftSpeed = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - HeaderSettings.RetractSpeed = value.Convert<float>(); - UpdateGCode(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) - { - HeaderSettings.BottomLightPWM = value.Convert<byte>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) - { - HeaderSettings.LayerLightPWM = value.Convert<byte>(); - UpdateGCode(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - using (var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update)) - { - foreach (var zipentry in outputFile.Entries) - { - if (zipentry.Name.EndsWith(".gcode")) - { - zipentry.Delete(); - break; - } - } - - outputFile.PutFileContent("run.gcode", GCode.ToString()); - - foreach (var layer in this) - { - if (!layer.IsModified) continue; - outputFile.PutFileContent(layer.Filename, layer.RawData); - layer.IsModified = false; - } - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - throw new NotImplementedException(); - } - - private void UpdateGCode() - { - string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; - GCode = new StringBuilder(); - GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); - - foreach (var propertyInfo in HeaderSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault(); - if (ReferenceEquals(displayNameAttribute, null)) continue; - GCode.AppendLine($";{displayNameAttribute.DisplayName}:{propertyInfo.GetValue(HeaderSettings)}"); - } - - GCode.AppendLine(); - GCode.AppendFormat(GCodeStart, Environment.NewLine); - - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - GCode.AppendLine($";LAYER_START:{layerIndex}"); - GCode.AppendLine($";currPos:{GetHeightFromLayer(layerIndex, false)}"); - GCode.AppendLine($"M6054 \"{layerIndex+1}.png\";show Image"); - GCode.AppendLine($"G0 Z{(layerIndex < InitialLayerCount ? HeaderSettings.BottomLayerLiftHeight : HeaderSettings.LayerLiftHeight) +GetHeightFromLayer(layerIndex, false)} F{(layerIndex < InitialLayerCount ? HeaderSettings.BottomLayerLiftSpeed : HeaderSettings.LayerLiftSpeed)};Z Lift"); - GCode.AppendLine($"G0 Z{GetHeightFromLayer(layerIndex)} F{HeaderSettings.RetractSpeed};Layer position"); - GCode.AppendLine($"G4 P{(layerIndex < InitialLayerCount ? HeaderSettings.BottomLightOffTime : HeaderSettings.LayerLightOffTime)*1000};Before cure delay"); - GCode.AppendLine($"M106 S{(layerIndex < InitialLayerCount ? HeaderSettings.BottomLightPWM : HeaderSettings.LayerLightPWM)};light on"); - GCode.AppendLine($"G4 P{(layerIndex < InitialLayerCount ? HeaderSettings.BottomLayerExposureTime : HeaderSettings.LayerExposureTime) * 1000};Cure time"); - GCode.AppendLine("M106 S0;light off"); - GCode.AppendLine(";LAYER_END"); - GCode.AppendLine(); - } - - GCode.AppendFormat(GCodeEnd, Environment.NewLine, HeaderSettings.MachineZ); - } - #endregion - } -} diff --git a/UVtools.Parser/Extensions/FileStreamExtensions.cs b/UVtools.Parser/Extensions/FileStreamExtensions.cs deleted file mode 100644 index f9ab1cc..0000000 --- a/UVtools.Parser/Extensions/FileStreamExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.IO; - -namespace UVtools.Parser.Extensions -{ - public static class FileStreamExtensions - { - public static uint ReadBytes(this FileStream fs, byte[] bytes, int offset = 0) - { - return (uint)fs.Read(bytes, offset, bytes.Length); - } - - public static uint WriteStream(this FileStream fs, MemoryStream stream, int offset = 0) - { - return fs.WriteBytes(stream.ToArray(), offset); - } - - public static uint WriteBytes(this FileStream fs, byte[] bytes, int offset = 0) - { - fs.Write(bytes, offset, bytes.Length); - return (uint)bytes.Length; - } - } -} diff --git a/UVtools.Parser/Extensions/StreamExtensions.cs b/UVtools.Parser/Extensions/StreamExtensions.cs deleted file mode 100644 index d46ba1d..0000000 --- a/UVtools.Parser/Extensions/StreamExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.IO; -using System.Runtime.Serialization.Formatters.Binary; - -namespace UVtools.Parser.Extensions -{ - public static class StreamExtensions - { - /// <summary> - /// Converts stream into byte array - /// </summary> - /// <param name="stream">Input</param> - /// <returns>Byte array data</returns> - public static byte[] ToArray(this Stream stream) - { - using (var memoryStream = new MemoryStream()) - { - stream.CopyTo(memoryStream); - return memoryStream.ToArray(); - } - } - } -} diff --git a/UVtools.Parser/Extensions/StringExtensions.cs b/UVtools.Parser/Extensions/StringExtensions.cs deleted file mode 100644 index 6cea2e0..0000000 --- a/UVtools.Parser/Extensions/StringExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.ComponentModel; -using System.Linq; - -namespace UVtools.Parser.Extensions -{ - public static class StringExtensions - { - /// <summary> - /// Upper the first character in a string - /// </summary> - /// <param name="input">Input string</param> - /// <returns>Modified string with fist character upper</returns> - public static string FirstCharToUpper(this string input) - { - switch (input) - { - case null: throw new ArgumentNullException(nameof(input)); - case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)); - default: return input.First().ToString().ToUpper() + input.Substring(1); - } - } - - /// <summary> - /// Converts a string into a target type - /// </summary> - /// <typeparam name="T">Target type to convert into</typeparam> - /// <param name="input">Value</param> - /// <returns>Converted value into target type</returns> - public static T Convert<T>(this string input) - { - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - //Cast ConvertFromString(string text) : object to (T) - return (T)converter.ConvertFromString(input); - } - return default(T); - } - } -} diff --git a/UVtools.Parser/Extensions/ZipArchiveExtensions.cs b/UVtools.Parser/Extensions/ZipArchiveExtensions.cs deleted file mode 100644 index b3c83f9..0000000 --- a/UVtools.Parser/Extensions/ZipArchiveExtensions.cs +++ /dev/null @@ -1,333 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; - -namespace UVtools.Parser.Extensions -{ - public static class ZipArchiveExtensions - { - /// <summary> - /// Used to specify what our overwrite policy - /// is for files we are extracting. - /// </summary> - public enum Overwrite - { - Always, - IfNewer, - Never - } - - /// <summary> - /// Used to identify what we will do if we are - /// trying to create a zip file and it already - /// exists. - /// </summary> - public enum ArchiveAction - { - Merge, - Replace, - Error, - Ignore - } - - /// <summary> - /// Unzips the specified file to the given folder in a safe - /// manner. This plans for missing paths and existing files - /// and handles them gracefully. - /// </summary> - /// <param name="sourceArchiveFileName"> - /// The name of the zip file to be extracted - /// </param> - /// <param name="destinationDirectoryName"> - /// The directory to extract the zip file to - /// </param> - /// <param name="overwriteMethod"> - /// Specifies how we are going to handle an existing file. - /// The default is IfNewer. - /// </param> - public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer) - { - //Opens the zip file up to be read - using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName)) - { - archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod); - } - } - - /// <summary> - /// Unzips the specified file to the given folder in a safe - /// manner. This plans for missing paths and existing files - /// and handles them gracefully. - /// </summary> - /// <param name="sourceArchiveFileName"> - /// The name of the zip file to be extracted - /// </param> - /// <param name="destinationDirectoryName"> - /// The directory to extract the zip file to - /// </param> - /// <param name="overwriteMethod"> - /// Specifies how we are going to handle an existing file. - /// The default is IfNewer. - /// </param> - public static void ImprovedExtractToDirectory(this ZipArchive archive, string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer) - { - //Loops through each file in the zip file - foreach (ZipArchiveEntry file in archive.Entries) - { - file.ImprovedExtractToFile(destinationDirectoryName, overwriteMethod); - } - } - - /// <summary> - /// Safely extracts a single file from a zip file - /// </summary> - /// <param name="file"> - /// The zip entry we are pulling the file from - /// </param> - /// <param name="destinationPath"> - /// The root of where the file is going - /// </param> - /// <param name="overwriteMethod"> - /// Specifies how we are going to handle an existing file. - /// The default is Overwrite.IfNewer. - /// </param> - public static void ImprovedExtractToFile(this ZipArchiveEntry file, string destinationPath, Overwrite overwriteMethod = Overwrite.IfNewer) - { - //Gets the complete path for the destination file, including any - //relative paths that were in the zip file - string destinationFileName = Path.Combine(destinationPath, file.FullName); - - //Gets just the new path, minus the file name so we can create the - //directory if it does not exist - string destinationFilePath = Path.GetDirectoryName(destinationFileName); - - //Creates the directory (if it doesn't exist) for the new path - Directory.CreateDirectory(destinationFilePath); - - //Determines what to do with the file based upon the - //method of overwriting chosen - switch (overwriteMethod) - { - case Overwrite.Always: - //Just put the file in and overwrite anything that is found - file.ExtractToFile(destinationFileName, true); - break; - case Overwrite.IfNewer: - //Checks to see if the file exists, and if so, if it should - //be overwritten - if (!File.Exists(destinationFileName) || File.GetLastWriteTime(destinationFileName) < file.LastWriteTime) - { - //Either the file didn't exist or this file is newer, so - //we will extract it and overwrite any existing file - file.ExtractToFile(destinationFileName, true); - } - break; - case Overwrite.Never: - //Put the file in if it is new but ignores the - //file if it already exists - if (!File.Exists(destinationFileName)) - { - file.ExtractToFile(destinationFileName); - } - break; - } - } - - /// <summary> - /// Allows you to add files to an archive, whether the archive - /// already exists or not - /// </summary> - /// <param name="archiveFullName"> - /// The name of the archive to you want to add your files to - /// </param> - /// <param name="files"> - /// A set of file names that are to be added - /// </param> - /// <param name="action"> - /// Specifies how we are going to handle an existing archive - /// </param> - /// <param name="compression"> - /// Specifies what type of compression to use - defaults to Optimal - /// </param> - public static void AddToArchive(string archiveFullName, - List<string> files, - ArchiveAction action = ArchiveAction.Replace, - Overwrite fileOverwrite = Overwrite.IfNewer, - CompressionLevel compression = CompressionLevel.Optimal) - { - //Identifies the mode we will be using - the default is Create - ZipArchiveMode mode = ZipArchiveMode.Create; - - //Determines if the zip file even exists - bool archiveExists = File.Exists(archiveFullName); - - //Figures out what to do based upon our specified overwrite method - switch (action) - { - case ArchiveAction.Merge: - //Sets the mode to update if the file exists, otherwise - //the default of Create is fine - if (archiveExists) - { - mode = ZipArchiveMode.Update; - } - break; - case ArchiveAction.Replace: - //Deletes the file if it exists. Either way, the default - //mode of Create is fine - if (archiveExists) - { - File.Delete(archiveFullName); - } - break; - case ArchiveAction.Error: - //Throws an error if the file exists - if (archiveExists) - { - throw new IOException(String.Format("The zip file {0} already exists.", archiveFullName)); - } - break; - case ArchiveAction.Ignore: - //Closes the method silently and does nothing - if (archiveExists) - { - return; - } - break; - } - - //Opens the zip file in the mode we specified - using (ZipArchive zipFile = ZipFile.Open(archiveFullName, mode)) - { - //This is a bit of a hack and should be refactored - I am - //doing a similar foreach loop for both modes, but for Create - //I am doing very little work while Update gets a lot of - //code. This also does not handle any other mode (of - //which there currently wouldn't be one since we don't - //use Read here). - if (mode == ZipArchiveMode.Create) - { - foreach (string file in files) - { - //Adds the file to the archive - zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression); - } - } - else - { - foreach (string file in files) - { - var fileInZip = (from f in zipFile.Entries - where f.Name == Path.GetFileName(file) - select f).FirstOrDefault(); - - switch (fileOverwrite) - { - case Overwrite.Always: - //Deletes the file if it is found - if (fileInZip != null) - { - fileInZip.Delete(); - } - - //Adds the file to the archive - zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression); - - break; - case Overwrite.IfNewer: - //This is a bit trickier - we only delete the file if it is - //newer, but if it is newer or if the file isn't already in - //the zip file, we will write it to the zip file - if (fileInZip != null) - { - //Deletes the file only if it is older than our file. - //Note that the file will be ignored if the existing file - //in the archive is newer. - if (fileInZip.LastWriteTime < File.GetLastWriteTime(file)) - { - fileInZip.Delete(); - - //Adds the file to the archive - zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression); - } - } - else - { - //The file wasn't already in the zip file so add it to the archive - zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression); - } - break; - case Overwrite.Never: - //Don't do anything - this is a decision that you need to - //consider, however, since this will mean that no file will - //be written. You could write a second copy to the zip with - //the same name (not sure that is wise, however). - break; - } - } - } - } - } - - /// <summary> - /// Get or put a file into archive - /// </summary> - /// <param name="input"><see cref="ZipArchive"/></param> - /// <param name="filename">Filename to create</param> - /// <returns>Created <see cref="ZipArchiveEntry"/></returns> - public static ZipArchiveEntry GetPutFile(this ZipArchive input, string filename) - { - return input.GetEntry(filename) ?? input.CreateEntry(filename); - } - - /// <summary> - /// Create or update a file into archive and write content to it - /// </summary> - /// <param name="input"><see cref="ZipArchive"/></param> - /// <param name="filename">Filename to create</param> - /// <param name="content">Content to write</param> - /// <returns>Created <see cref="ZipArchiveEntry"/></returns> - public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, string content) - { - ZipArchiveEntry entry = input.GetEntry(filename) ?? input.CreateEntry(filename); - - if (string.IsNullOrEmpty(content)) return entry; - Stream stream = entry.Open(); - stream.SetLength(0); - using (TextWriter tw = new StreamWriter(stream)) - { - tw.Write(content); - tw.Close(); - } - return entry; - } - - /// <summary> - /// Create or update a file into archive and write content to it - /// </summary> - /// <param name="input"><see cref="ZipArchive"/></param> - /// <param name="filename">Filename to create</param> - /// <param name="content">Content to write</param> - /// <returns>Created <see cref="ZipArchiveEntry"/></returns> - public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, byte[] content) - { - ZipArchiveEntry entry = input.GetEntry(filename) ?? input.CreateEntry(filename); - - if (ReferenceEquals(content, null)) return entry; - Stream stream = entry.Open(); - stream.SetLength(0); - stream.Write(content, 0, content.Length); - return entry; - } - } -} diff --git a/UVtools.Parser/FileExtension.cs b/UVtools.Parser/FileExtension.cs deleted file mode 100644 index cb56563..0000000 --- a/UVtools.Parser/FileExtension.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.Collections.Generic; - -namespace UVtools.Parser -{ - /// <summary> - /// Represents a file extension for slicer file formats - /// </summary> - public sealed class FileExtension - { - #region Properties - /// <summary> - /// Gets the extension name without the dot (.) - /// </summary> - public string Extension { get; } - - /// <summary> - /// Gets the extension description - /// </summary> - public string Description { get; } - - /// <summary> - /// Gets the file filter for open and save dialogs - /// </summary> - public string Filter => $@"{Description} (*.{Extension})|*.{Extension}"; - #endregion - - #region Constructor - /// <summary> - /// Constructor - /// </summary> - /// <param name="extension">The extension name without the dot (.)</param> - /// <param name="description">The extension description</param> - public FileExtension(string extension, string description) - { - Extension = extension; - Description = description; - } - #endregion - - #region Overrides - - public override string ToString() - { - return $"{nameof(Extension)}: {Extension}, {nameof(Description)}: {Description}"; - } - - private bool Equals(FileExtension other) - { - return Extension == other.Extension; - } - - public override bool Equals(object obj) - { - return ReferenceEquals(this, obj) || obj is FileExtension other && Equals(other); - } - - public override int GetHashCode() - { - return (Extension != null ? Extension.GetHashCode() : 0); - } - - private sealed class ExtensionEqualityComparer : IEqualityComparer<FileExtension> - { - public bool Equals(FileExtension x, FileExtension y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x.Extension == y.Extension; - } - - public int GetHashCode(FileExtension obj) - { - return (obj.Extension != null ? obj.Extension.GetHashCode() : 0); - } - } - - public static IEqualityComparer<FileExtension> ExtensionComparer { get; } = new ExtensionEqualityComparer(); - #endregion - } -} diff --git a/UVtools.Parser/FileFormat.cs b/UVtools.Parser/FileFormat.cs deleted file mode 100644 index 03374fe..0000000 --- a/UVtools.Parser/FileFormat.cs +++ /dev/null @@ -1,724 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using UVtools.Parser; -using UVtools.Parser.Extensions; -using Size = System.Drawing.Size; - -namespace UVtools.Parser -{ - /// <summary> - /// Slicer <see cref="FileFormat"/> representation - /// </summary> - public abstract class FileFormat : IFileFormat, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer> - { - #region Enums - - /// <summary> - /// Enumeration of file format types - /// </summary> - public enum FileFormatType : byte - { - Archive, - Binary - } - - /// <summary> - /// Enumeration of file thumbnail size types - /// </summary> - public enum FileThumbnailSize : byte - { - Small = 0, - Large - } - #endregion - - #region Sub Classes - /// <summary> - /// Available Print Parameters to modify - /// </summary> - public class PrintParameterModifier - { - - #region Instances - public static PrintParameterModifier InitialLayerCount { get; } = new PrintParameterModifier("Initial Layer Count", @"Modify 'Initial Layer Count' value", null,0, ushort.MaxValue); - public static PrintParameterModifier InitialExposureSeconds { get; } = new PrintParameterModifier("Initial Exposure Time", @"Modify 'Initial Exposure Time' seconds", "s", 0.1M, byte.MaxValue); - public static PrintParameterModifier ExposureSeconds { get; } = new PrintParameterModifier("Exposure Time", @"Modify 'Exposure Time' seconds", "s", 0.1M, byte.MaxValue); - - public static PrintParameterModifier BottomLayerOffTime { get; } = new PrintParameterModifier("Bottom Layer Off Time", @"Modify 'Bottom Layer Off Time' seconds", "s"); - public static PrintParameterModifier LayerOffTime { get; } = new PrintParameterModifier("Layer Off Time", @"Modify 'Layer Off Time' seconds", "s"); - public static PrintParameterModifier BottomLiftHeight { get; } = new PrintParameterModifier("Bottom Lift Height", @"Modify 'Bottom Lift Height' millimeters between bottom layers", "mm"); - public static PrintParameterModifier BottomLiftSpeed { get; } = new PrintParameterModifier("Bottom Lift Speed", @"Modify 'Bottom Lift Speed' mm/min between bottom layers", "mm/min"); - public static PrintParameterModifier LiftHeight { get; } = new PrintParameterModifier("Lift Height", @"Modify 'Lift Height' millimeters between layers", "mm"); - public static PrintParameterModifier LiftSpeed { get; } = new PrintParameterModifier("Lift Speed", @"Modify 'Lift Speed' mm/min between layers", "mm/min", 10, 5000); - public static PrintParameterModifier RetractSpeed { get; } = new PrintParameterModifier("Retract Speed", @"Modify 'Retract Speed' mm/min between layers", "mm/min", 10, 5000); - - public static PrintParameterModifier BottomLightPWM { get; } = new PrintParameterModifier("Bottom Light PWM", @"Modify 'Bottom Light PWM' value", null, 50, byte.MaxValue); - public static PrintParameterModifier LightPWM { get; } = new PrintParameterModifier("Light PWM", @"Modify 'Light PWM' value", null, 50, byte.MaxValue); - #endregion - - #region Properties - - /// <summary> - /// Gets the name - /// </summary> - public string Name { get; } - - /// <summary> - /// Gets the description - /// </summary> - public string Description { get; } - - /// <summary> - /// Gets the value unit - /// </summary> - public string ValueUnit { get; } - - /// <summary> - /// Gets the minimum value - /// </summary> - public decimal Minimum { get; } - - /// <summary> - /// Gets the maximum value - /// </summary> - public decimal Maximum { get; } - #endregion - - #region Constructor - public PrintParameterModifier(string name, string description, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000) - { - Name = name; - Description = description; - ValueUnit = valueUnit ?? string.Empty; - Minimum = minimum; - Maximum = maximum; - } - #endregion - - #region Overrides - public override string ToString() - { - return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}"; - } - #endregion - } - #endregion - - #region Constants - private const string ExtractConfigFileName = "Configuration"; - private const string ExtractConfigFileExtension = "ini"; - #endregion - - #region Static Methods - /// <summary> - /// Gets the available formats to process - /// </summary> - public static FileFormat[] AvaliableFormats { get; } = - { - new SL1File(), // Prusa SL1 - new ChituboxZipFile(), // Zip - new ChituboxFile(), // cbddlp, cbt, photon - new PHZFile(), // phz - new PWSFile(), // PSW - new ZCodexFile(), // zcodex - new CWSFile(), // CWS - new ImageFile(), // images - }; - - /// <summary> - /// Gets all filters for open and save file dialogs - /// </summary> - public static string AllFileFilters => - AvaliableFormats.Aggregate(string.Empty, - (current, fileFormat) => string.IsNullOrEmpty(current) - ? fileFormat.FileFilter - : $"{current}|" + fileFormat.FileFilter) - + - AvaliableFormats.Aggregate("|All slicer files|", - (current, fileFormat) => current.EndsWith("|") - ? $"{current}{fileFormat.FileFilterExtensionsOnly}" - : $"{current};{fileFormat.FileFilterExtensionsOnly}"); - - /// <summary> - /// Gets the count of available file extensions - /// </summary> - public static byte FileExtensionsCount - { - get - { - return AvaliableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length)); - } - } - - /// <summary> - /// Find <see cref="FileFormat"/> by an extension - /// </summary> - /// <param name="extension">Extension name to find</param> - /// <param name="isFilePath">True if <see cref="extension"/> is a file path rather than only a extension name</param> - /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param> - /// <returns><see cref="FileFormat"/> object or null if not found</returns> - public static FileFormat FindByExtension(string extension, bool isFilePath = false, bool createNewInstance = false) - { - return (from fileFormat in AvaliableFormats where fileFormat.IsExtensionValid(extension, isFilePath) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault(); - } - - /// <summary> - /// Find <see cref="FileFormat"/> by an type - /// </summary> - /// <param name="type">Type to find</param> - /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param> - /// <returns><see cref="FileFormat"/> object or null if not found</returns> - public static FileFormat FindByType(Type type, bool createNewInstance = false) - { - return (from t in AvaliableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault(); - } - #endregion - - #region Properties - - public abstract FileFormatType FileType { get; } - - public abstract FileExtension[] FileExtensions { get; } - public abstract Type[] ConvertToFormats { get; } - - public abstract PrintParameterModifier[] PrintParameterModifiers { get; } - - public string FileFilter { - get - { - var result = string.Empty; - - foreach (var fileExt in FileExtensions) - { - if (!ReferenceEquals(result, string.Empty)) - { - result += '|'; - } - result += fileExt.Filter; - } - - return result; - } - } - - public string FileFilterExtensionsOnly - { - get - { - var result = string.Empty; - - foreach (var fileExt in FileExtensions) - { - if (!ReferenceEquals(result, string.Empty)) - { - result += ';'; - } - result += $"*.{fileExt.Extension}"; - } - - return result; - } - } - - public string FileFullPath { get; set; } - - public abstract byte ThumbnailsCount { get; } - - public byte CreatedThumbnailsCount { - get - { - if (ReferenceEquals(Thumbnails, null)) return 0; - byte count = 0; - - foreach (var thumbnail in Thumbnails) - { - if (ReferenceEquals(thumbnail, null)) continue; - count++; - } - - return count; - } - } - - public abstract Size[] ThumbnailsOriginalSize { get; } - - public Image<Rgba32>[] Thumbnails { get; set; } - public LayerManager LayerManager { get; set; } - - /// <summary> - /// Gets if any layer got modified - /// </summary> - public bool ModifiedLayers => LayerManager.IsModified; - - public abstract uint ResolutionX { get; } - - public abstract uint ResolutionY { get; } - public bool HaveAntiAliasing => AntiAliasing > 1; - public abstract byte AntiAliasing { get; } - - public abstract float LayerHeight { get; } - - public float TotalHeight => (float)Math.Round(LayerCount * LayerHeight, 2); - - public uint LayerCount => LayerManager.Count; - - public abstract ushort InitialLayerCount { get; } - - public abstract float InitialExposureTime { get; } - - public abstract float LayerExposureTime { get; } - - public abstract float LiftHeight { get; } - - public abstract float RetractSpeed { get; } - - public abstract float LiftSpeed { get; } - - public abstract float PrintTime { get; } - - public abstract float UsedMaterial { get; } - - public abstract float MaterialCost { get; } - - public abstract string MaterialName { get; } - - public abstract string MachineName { get; } - - public StringBuilder GCode { get; set; } - - public abstract object[] Configs { get; } - - public bool IsValid => !ReferenceEquals(FileFullPath, null); - #endregion - - #region Constructor - protected FileFormat() - { - Thumbnails = new Image<Rgba32>[ThumbnailsCount]; - } - #endregion - - #region Indexers - public Layer this[int index] - { - get => LayerManager[index]; - set => LayerManager[index] = value; - } - - public Layer this[uint index] - { - get => LayerManager[index]; - set => LayerManager[index] = value; - } - - public Layer this[long index] - { - get => LayerManager[index]; - set => LayerManager[index] = value; - } - #endregion - - #region Numerators - public IEnumerator<Layer> GetEnumerator() - { - return ((IEnumerable<Layer>)LayerManager.Layers).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion - - #region Overrides - public override bool Equals(object obj) - { - return Equals(obj as FileFormat); - } - - public bool Equals(FileFormat other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return FileFullPath.Equals(other.FileFullPath); - } - - public override int GetHashCode() - { - return (FileFullPath != null ? FileFullPath.GetHashCode() : 0); - } - - public void Dispose() - { - Clear(); - } - - #endregion - - #region Methods - public virtual void Clear() - { - FileFullPath = null; - LayerManager = null; - GCode = null; - - if (!ReferenceEquals(Thumbnails, null)) - { - for (int i = 0; i < ThumbnailsCount; i++) - { - Thumbnails[i]?.Dispose(); - } - } - - - } - - public void FileValidation(string fileFullPath) - { - if (ReferenceEquals(fileFullPath, null)) throw new ArgumentNullException(nameof(FileFullPath), "fullFilePath can't be null."); - if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath); - - if (IsExtensionValid(fileFullPath, true)) - { - return; - } - - throw new FileLoadException($"The specified file is not valid.", fileFullPath); - } - - public bool IsExtensionValid(string extension, bool isFilePath = false) - { - extension = isFilePath ? Path.GetExtension(extension)?.Remove(0, 1) : extension; - return FileExtensions.Any(fileExtension => fileExtension.Extension.Equals(extension, StringComparison.InvariantCultureIgnoreCase)); - } - - public string GetFileExtensions(string prepend = ".", string separator = ", ") - { - var result = string.Empty; - - foreach (var fileExt in FileExtensions) - { - if (!ReferenceEquals(result, string.Empty)) - { - result += separator; - } - result += $"{prepend}{fileExt.Extension}"; - } - - return result; - } - - public Image<Rgba32> GetThumbnail(uint maxHeight = 400) - { - for (int i = 0; i < ThumbnailsCount; i++) - { - if(ReferenceEquals(Thumbnails[i], null)) continue; - if (Thumbnails[i].Height <= maxHeight) return Thumbnails[i]; - } - - return null; - } - - public void SetThumbnails(Image<Rgba32>[] images) - { - for (var i = 0; i < ThumbnailsCount; i++) - { - Thumbnails[i] = images[Math.Min(i, images.Length - 1)].Clone(); - } - } - - public void SetThumbnails(Image<Rgba32> image) - { - for (var i = 0; i < ThumbnailsCount; i++) - { - Thumbnails[i] = image.Clone(); - } - } - - public virtual void Encode(string fileFullPath) - { - FileFullPath = fileFullPath; - - if (File.Exists(fileFullPath)) - { - File.Delete(fileFullPath); - } - - for (var i = 0; i < Thumbnails.Length; i++) - { - if (ReferenceEquals(Thumbnails[i], null)) continue; - - Thumbnails[i].Mutate(x => x.Resize(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height)); - } - } - - /*public virtual void BeginEncode(string fileFullPath) - { - } - - - public abstract void InsertLayerImageEncode(Image<L8> image, uint layerIndex); - - public abstract void EndEncode();*/ - - public virtual void Decode(string fileFullPath) - { - Clear(); - FileValidation(fileFullPath); - FileFullPath = fileFullPath; - } - - public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true) - { - /*if (emptyFirst) - { - if (Directory.Exists(path)) - { - DirectoryInfo di = new DirectoryInfo(path); - - foreach (FileInfo file in di.GetFiles()) - { - file.Delete(); - } - foreach (DirectoryInfo dir in di.GetDirectories()) - { - dir.Delete(true); - } - } - }*/ - - //if (!Directory.Exists(path)) - //{ - Directory.CreateDirectory(path); - //} - - - if (FileType == FileFormatType.Archive) - { - //ZipFile.ExtractToDirectory(FileFullPath, path); - ZipArchiveExtensions.ImprovedExtractToDirectory(FileFullPath, path, ZipArchiveExtensions.Overwrite.Always); - return; - } - - if (genericConfigExtract) - { - if (!ReferenceEquals(Configs, null)) - { - using (TextWriter tw = new StreamWriter(Path.Combine(path, $"{ExtractConfigFileName}.{ExtractConfigFileExtension}"), false)) - { - foreach (var config in Configs) - { - var type = config.GetType(); - tw.WriteLine($"[{type.Name}]"); - foreach (var property in type.GetProperties()) - { - tw.WriteLine($"{property.Name} = {property.GetValue(config)}"); - } - - tw.WriteLine(); - } - - tw.Close(); - } - } - } - - if (genericLayersExtract) - { - uint i = 0; - if (!ReferenceEquals(Thumbnails, null)) - { - foreach (var thumbnail in Thumbnails) - { - if (ReferenceEquals(thumbnail, null)) - { - continue; - } - - thumbnail.Save(Path.Combine(path, $"Thumbnail{i}.png"), Helpers.PngEncoder); - i++; - } - } - - if (LayerCount > 0) - { - Parallel.ForEach(this, (layer) => - { - var byteArr = layer.RawData; - using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layer.Index}.png"), - byteArr.Length)) - { - stream.Write(byteArr, 0, byteArr.Length); - stream.Close(); - } - }); - } - - /* Parallel.For(0, LayerCount, layerIndex => { - var byteArr = this[layerIndex].RawData; - using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layerIndex}.png"), byteArr.Length)) - { - stream.Write(byteArr, 0, byteArr.Length); - stream.Close(); - } - });*/ - /*for (i = 0; i < LayerCount; i++) - { - var byteArr = GetLayer(i); - using (FileStream stream = File.Create(Path.Combine(path, $"Layer{i}.png"), byteArr.Length)) - { - stream.Write(byteArr, 0, byteArr.Length); - stream.Close(); - } - }*/ - } - } - - public virtual float GetHeightFromLayer(uint layerIndex, bool realHeight = true) - { - return (float)Math.Round((layerIndex+(realHeight ? 1 : 0)) * LayerHeight, 2); - } - - public T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue) - { - return layerIndex < InitialLayerCount ? initialLayerValue : normalLayerValue; - } - - public virtual object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - return InitialLayerCount; - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - return InitialExposureTime; - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - return LayerExposureTime; - - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - return LiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - return LiftSpeed; - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - return RetractSpeed; - - - - return null; - } - - public virtual bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, object value) - { - return SetValueFromPrintParameterModifier(modifier, value.ToString()); - } - - public abstract bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value); - - public void Save() - { - SaveAs(); - } - - public abstract void SaveAs(string filePath = null); - - public abstract bool Convert(Type to, string fileFullPath); - public bool Convert(FileFormat to, string fileFullPath) - { - return Convert(to.GetType(), fileFullPath); - } - - public void Resize(uint startLayerIndex, uint endLayerIndex, float x, float y, bool fade) - { - if (x == 1f && y == 1f) return; - - Parallel.For(startLayerIndex, endLayerIndex+1, /*new ParallelOptions { MaxDegreeOfParallelism = 1 },*/ layerIndex => - { - var newX = x; - var newY = y; - if (fade) - { - if (newX != 1f) - { - double steps = Math.Abs(newX - 1.0) / (endLayerIndex - startLayerIndex); - //maxIteration = Math.Max(iterationsStart, iterationsEnd); - - newX = (float) (newX < 1f - ? newX + (layerIndex - startLayerIndex) * steps - : newX - (layerIndex - startLayerIndex) * steps); - - // constrain - //iterations = Math.Min(Math.Max(1, iterations), maxIteration); - } - - if (y != 1f) - { - double steps = Math.Abs(newY - 1.0) / (endLayerIndex - startLayerIndex); - //maxIteration = Math.Max(iterationsStart, iterationsEnd); - - newY = (float) (newY < 1f - ? newY + (layerIndex - startLayerIndex) * steps - : newY - (layerIndex - startLayerIndex) * steps); - - // constrain - //iterations = Math.Min(Math.Max(1, iterations), maxIteration); - } - } - - if (newX == 1f && newY == 1f) return; - - var image = this[layerIndex].Image; - var newImage = new Image<L8>(image.GetConfiguration(), image.Width, image.Height); - - int width = (int)(image.Width * newX); - int height = (int)(image.Height * newY); - Point location = new Point(image.Width/2 - width/2, image.Height/2 - height/2); - - - image.Mutate(o => o.Resize(width, height)); - newImage.Mutate(o => o.DrawImage(image, location, 1f)); - - - this[layerIndex].Image = newImage; - }); - - /*for (uint layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++) - { - var image = this[layerIndex].Image; - var newImage = new Image<L8>(image.GetConfiguration(), image.Width, image.Height); - - int width = (int)(image.Width * x); - int height = (int)(image.Height * y); - Point location = new Point(image.Width / 2 - width / 2, image.Height / 2 - height / 2); - - - image.Mutate(o => o.Resize(width, height)); - newImage.Mutate(o => o.DrawImage(image, location, 1f)); - - this[layerIndex].Image = newImage; - }*/ - } - - public byte ValidateAntiAliasingLevel() - { - if (AntiAliasing < 2) return 1; - if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing)); - return AntiAliasing; - } - - #endregion - } -} diff --git a/UVtools.Parser/Helpers.cs b/UVtools.Parser/Helpers.cs deleted file mode 100644 index ef0262d..0000000 --- a/UVtools.Parser/Helpers.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using BinarySerialization; -using Newtonsoft.Json; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; - -namespace UVtools.Parser -{ - /// <summary> - /// A helper class with utilities - /// </summary> - public static class Helpers - { - public static PngEncoder PngEncoder { get; } = new PngEncoder(); - public static BmpEncoder BmpEncoder { get; } = new BmpEncoder{SupportTransparency = true, BitsPerPixel = BmpBitsPerPixel.Pixel32}; - /// <summary> - /// Gets the <see cref="BinarySerializer"/> instance - /// </summary> - public static BinarySerializer Serializer { get; } = new BinarySerializer {Endianness = Endianness.Little }; - - /// <summary> - /// Gets a white color of <see cref="L8"/> - /// </summary> - public static L8 L8White { get; } = new L8(255); - public static L8 L8Black { get; } = new L8(0); - - public static byte[] ImageL8ToBytes(Image<L8> image) - { - return image.TryGetSinglePixelSpan(out var pixelSpan) ? MemoryMarshal.AsBytes(pixelSpan).ToArray() : null; - } - - /*public static T ByteToType<T>(BinaryReader reader) - { - byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); - - GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - handle.Free(); - - return theStructure; - } - - public static byte[] SerializeToBytes<T>(T item) - { - var formatter = new BinaryFormatter(); - using (var stream = new MemoryStream()) - { - formatter.Serialize(stream, item); - stream.Seek(0, SeekOrigin.Begin); - return stream.ToArray(); - } - }*/ - - public static MemoryStream Serialize(object value) - { - MemoryStream stream = new MemoryStream(); - Serializer.Serialize(stream, value); - return stream; - } - - public static T Deserialize<T>(Stream stream) - { - return Serializer.Deserialize<T>(stream); - } - - public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0) - { - using (MemoryStream stream = Helpers.Serialize(value)) - { - return fs.WriteStream(stream, offset); - } - } - - public static T JsonDeserializeObject<T>(Stream stream) - { - using (TextReader tr = new StreamReader(stream)) - { - return JsonConvert.DeserializeObject<T>(tr.ReadToEnd()); - } - } - - public static SHA1CryptoServiceProvider SHA1 { get; } = new SHA1CryptoServiceProvider(); - public static string ComputeSHA1Hash(byte[] input) - { - return Convert.ToBase64String(SHA1.ComputeHash(input)); - } - - public static bool SetPropertyValue(PropertyInfo attribute, object obj, string value) - { - var name = attribute.PropertyType.Name.ToLower(); - switch (name) - { - case "string": - attribute.SetValue(obj, value.Convert<string>()); - return true; - case "boolean": - if(char.IsDigit(value[0])) - attribute.SetValue(obj, !value.Equals(0)); - else - attribute.SetValue(obj, value.Equals("True", StringComparison.InvariantCultureIgnoreCase)); - return true; - case "byte": - attribute.SetValue(obj, value.Convert<byte>()); - return true; - case "uint16": - attribute.SetValue(obj, value.Convert<ushort>()); - return true; - case "uint32": - attribute.SetValue(obj, value.Convert<uint>()); - return true; - case "single": - attribute.SetValue(obj, (float)Math.Round(float.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3)); - return true; - case "double": - attribute.SetValue(obj, Math.Round(double.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3)); - return true; - case "decimal": - attribute.SetValue(obj, Math.Round(decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3)); - return true; - default: - throw new Exception($"Data type '{name}' not recognized, contact developer."); - } - } - } -} diff --git a/UVtools.Parser/IFileFormat.cs b/UVtools.Parser/IFileFormat.cs deleted file mode 100644 index a2412d8..0000000 --- a/UVtools.Parser/IFileFormat.cs +++ /dev/null @@ -1,357 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Text; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace UVtools.Parser -{ - /// <summary> - /// Slicer file format representation interface - /// </summary> - public interface IFileFormat - { - #region Properties - /// <summary> - /// Gets the file format type - /// </summary> - FileFormat.FileFormatType FileType { get; } - - /// <summary> - /// Gets the valid file extensions for this <see cref="FileFormat"/> - /// </summary> - FileExtension[] FileExtensions { get; } - - /// <summary> - /// Gets the implemented file formats able to convert to - /// </summary> - Type[] ConvertToFormats { get; } - - /// <summary> - /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> - /// </summary> - FileFormat.PrintParameterModifier[] PrintParameterModifiers { get; } - - /// <summary> - /// Gets the file filter for open and save dialogs - /// </summary> - string FileFilter { get; } - - /// <summary> - /// Gets all valid file extensions in "*.extension1;*.extension2" format - /// </summary> - - string FileFilterExtensionsOnly { get; } - - /// <summary> - /// Gets the input file path loaded into this <see cref="FileFormat"/> - /// </summary> - string FileFullPath { get; set; } - - /// <summary> - /// Gets the thumbnails count present in this file format - /// </summary> - byte ThumbnailsCount { get; } - - /// <summary> - /// Gets the number of created thumbnails - /// </summary> - byte CreatedThumbnailsCount { get; } - - /// <summary> - /// Gets the original thumbnail sizes - /// </summary> - System.Drawing.Size[] ThumbnailsOriginalSize { get; } - - /// <summary> - /// Gets the thumbnails for this <see cref="FileFormat"/> - /// </summary> - Image<Rgba32>[] Thumbnails { get; set; } - - /// <summary> - /// Gets the cached layers into compressed bytes - /// </summary> - LayerManager LayerManager { get; set; } - - /// <summary> - /// Gets the image width resolution - /// </summary> - uint ResolutionX { get; } - - /// <summary> - /// Gets the image height resolution - /// </summary> - uint ResolutionY { get; } - - bool HaveAntiAliasing { get; } - - /// <summary> - /// Gets the AntiAliasing level - /// </summary> - byte AntiAliasing { get; } - - /// <summary> - /// Gets Layer Height in mm - /// </summary> - float LayerHeight { get; } - - /// <summary> - /// Gets Total Height in mm - /// </summary> - float TotalHeight { get; } - - /// <summary> - /// Gets the number of layers present in this file - /// </summary> - uint LayerCount { get; } - - /// <summary> - /// Gets the number of initial layer count - /// </summary> - /// </summary> - ushort InitialLayerCount { get; } - - /// <summary> - /// Gets the initial exposure time for <see cref="InitialLayerCount"/> - /// </summary> - float InitialExposureTime { get; } - - /// <summary> - /// Gets the normal layer exposure time - /// </summary> - float LayerExposureTime { get; } - - /// <summary> - /// Gets the speed in mm/min for the detracts - /// </summary> - float LiftSpeed { get; } - - /// <summary> - /// Gets the height in mm to retract between layers - /// </summary> - float LiftHeight { get; } - - /// <summary> - /// Gets the speed in mm/min for the retracts - /// </summary> - float RetractSpeed { get; } - - /// <summary> - /// Gets the estimate print time in seconds - /// </summary> - float PrintTime { get; } - - /// <summary> - /// Gets the estimate used material in ml - /// </summary> - float UsedMaterial { get; } - - /// <summary> - /// Gets the estimate material cost - /// </summary> - float MaterialCost { get; } - - /// <summary> - /// Gets the material name - /// </summary> - string MaterialName { get; } - - /// <summary> - /// Gets the machine name - /// </summary> - string MachineName { get; } - - /// <summary> - /// Gets the GCode, returns null if not supported - /// </summary> - StringBuilder GCode { get; set; } - - /// <summary> - /// Get all configuration objects with properties and values - /// </summary> - object[] Configs { get; } - - /// <summary> - /// Gets if this file is valid to read - /// </summary> - bool IsValid { get; } - - #endregion - - #region Methods - /// <summary> - /// Clears all definitions and properties, it also dispose valid candidates - /// </summary> - void Clear(); - - /// <summary> - /// Validate if a file is a valid <see cref="FileFormat"/> - /// </summary> - /// <param name="fileFullPath">Full file path</param> - void FileValidation(string fileFullPath); - - /// <summary> - /// Checks if a extension is valid under the <see cref="FileFormat"/> - /// </summary> - /// <param name="extension">Extension to check</param> - /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param> - /// <returns>True if valid, otherwise false</returns> - bool IsExtensionValid(string extension, bool isFilePath = false); - - /// <summary> - /// Gets all valid file extensions in a specified format - /// </summary> - - string GetFileExtensions(string prepend = ".", string separator = ", "); - - /// <summary> - /// Gets a thumbnail by it height or lower - /// </summary> - /// <param name="maxHeight">Max height allowed</param> - /// <returns></returns> - Image<Rgba32> GetThumbnail(uint maxHeight = 400); - - /// <summary> - /// Sets thumbnails from a list of thumbnails and clone them - /// </summary> - /// <param name="images"></param> - void SetThumbnails(Image<Rgba32>[] images); - - /// <summary> - /// Sets all thumbnails the same image - /// </summary> - /// <param name="images">Image to set</param> - void SetThumbnails(Image<Rgba32> images); - - /// <summary> - /// Encode to an output file - /// </summary> - /// <param name="fileFullPath">Output file</param> - void Encode(string fileFullPath); - - /* - /// <summary> - /// Begin encode to an output file - /// </summary> - /// <param name="fileFullPath">Output file</param> - //void BeginEncode(string fileFullPath); - - /// <summary> - /// Insert a layer image to be encoded - /// </summary> - /// <param name="image"></param> - /// <param name="layerIndex"></param> - //void InsertLayerImageEncode(Image<L8> image, uint layerIndex); - - /// <summary> - /// Finish the encoding procedure - /// </summary> - //void EndEncode();*/ - - /// <summary> - /// Decode a slicer file - /// </summary> - /// <param name="fileFullPath"></param> - void Decode(string fileFullPath); - - /// <summary> - /// Extract contents to a folder - /// </summary> - /// <param name="path">Path to folder where content will be extracted</param> - /// <param name="genericConfigExtract"></param> - /// <param name="genericLayersExtract"></param> - void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true); - - /// <summary> - /// Get height in mm from layer height - /// </summary> - /// <param name="layerIndex"></param> - /// <param name="realHeight"></param> - /// <returns>The height in mm</returns> - float GetHeightFromLayer(uint layerIndex, bool realHeight = true); - - /// <summary> - /// Gets the value for initial layer or normal layers based on layer index - /// </summary> - /// <typeparam name="T">Type of value</typeparam> - /// <param name="layerIndex">Layer index</param> - /// <param name="initialLayerValue">Initial value</param> - /// <param name="normalLayerValue">Normal value</param> - /// <returns></returns> - T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue); - - /// <summary> - /// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/> - /// </summary> - /// <param name="modifier">Modifier to use</param> - /// <returns>A value</returns> - object GetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier); - - /// <summary> - /// Sets a property value attributed to <see cref="modifier"/> - /// </summary> - /// <param name="modifier">Modifier to use</param> - /// <param name="value">Value to set</param> - /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns> - bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, object value); - - /// <summary> - /// Sets a property value attributed to <see cref="modifier"/> - /// </summary> - /// <param name="modifier">Modifier to use</param> - /// <param name="value">Value to set</param> - /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns> - bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, string value); - - /// <summary> - /// Saves current configuration on input file - /// </summary> - void Save(); - - /// <summary> - /// Saves current configuration on a copy - /// </summary> - /// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param> - void SaveAs(string filePath = null); - - /// <summary> - /// Converts this file type to another file type - /// </summary> - /// <param name="to">Target file format</param> - /// <param name="fileFullPath">Output path file</param> - /// <returns>True if convert succeed, otherwise false</returns> - bool Convert(Type to, string fileFullPath); - - /// <summary> - /// Converts this file type to another file type - /// </summary> - /// <param name="to">Target file format</param> - /// <param name="fileFullPath">Output path file</param> - /// <returns>True if convert succeed, otherwise false</returns> - bool Convert(FileFormat to, string fileFullPath); - - /// <summary> - /// Resizes layer images in x and y factor, starting at 1 = 100% - /// </summary> - /// <param name="startLayerIndex">Layer index to start</param> - /// <param name="endLayerIndex">Layer index to end</param> - /// <param name="x">X factor, starts at 1</param> - /// <param name="y">Y factor, starts at 1</param> - /// <param name="fade">Fade X/Y towards 100%</param> - void Resize(uint startLayerIndex, uint endLayerIndex, float x, float y, bool fade); - - /// <summary> - /// Validate AntiAlias Level - /// </summary> - byte ValidateAntiAliasingLevel(); - - #endregion - } -} diff --git a/UVtools.Parser/ImageFile.cs b/UVtools.Parser/ImageFile.cs deleted file mode 100644 index 09e788f..0000000 --- a/UVtools.Parser/ImageFile.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.IO; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using Size = System.Drawing.Size; - -namespace UVtools.Parser -{ - public class ImageFile : FileFormat - { - public override FileFormatType FileType { get; } = FileFormatType.Binary; - - public override FileExtension[] FileExtensions { get; } = - { - new FileExtension("jpg", "JPG"), - new FileExtension("jpeg", "JPEG"), - new FileExtension("png", "PNG"), - new FileExtension("bmp", "BMP"), - new FileExtension("gif", "GIF"), - new FileExtension("tga", "TGA"), - }; - - public override Type[] ConvertToFormats { get; } = null; - public override PrintParameterModifier[] PrintParameterModifiers { get; } = null; - public override byte ThumbnailsCount { get; } = 4; - public override Size[] ThumbnailsOriginalSize { get; } = null; - public override uint ResolutionX => (uint)ImageL8.Width; - public override uint ResolutionY => (uint)ImageL8.Height; - public override byte AntiAliasing { get; } = 1; - public override float LayerHeight { get; } = 0; - public override ushort InitialLayerCount { get; } = 1; - public override float InitialExposureTime { get; } = 0; - public override float LayerExposureTime { get; } = 0; - public override float LiftHeight { get; } = 0; - public override float RetractSpeed { get; } = 0; - public override float LiftSpeed { get; } = 0; - public override float PrintTime { get; } = 0; - public override float UsedMaterial { get; } = 0; - public override float MaterialCost { get; } = 0; - public override string MaterialName { get; } = null; - public override string MachineName { get; } = null; - public override object[] Configs { get; } = null; - - private Image<L8> ImageL8 { get; set; } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - throw new NotImplementedException(); - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - using (var ms = new MemoryStream()) - { - using (var fs = File.Open(fileFullPath, FileMode.Open)) - { - fs.CopyTo(ms); - ms.Seek(0, SeekOrigin.Begin); - - var image = Image.Load(ms); - const byte startDivisor = 2; - for (int i = 0; i < ThumbnailsCount; i++) - { - Thumbnails[i] = image.CloneAs<Rgba32>(); - var divisor = (i + startDivisor); - Thumbnails[i].Mutate(o => o.Resize(image.Width / divisor, image.Height / divisor)); - } - image.Mutate(o => o.Grayscale()); - ImageL8 = image.CloneAs<L8>(); - - LayerManager = new LayerManager(1); - this[0] = new Layer(0, ImageL8, Path.GetFileName(fileFullPath)); - - fs.Close(); - } - } - } - - public override void SaveAs(string filePath = null) - { - this[0].Image.Save(filePath); - } - - public override bool Convert(Type to, string fileFullPath) - { - throw new NotImplementedException(); - } - - - } -} diff --git a/UVtools.Parser/LayerManager.cs b/UVtools.Parser/LayerManager.cs deleted file mode 100644 index fa03636..0000000 --- a/UVtools.Parser/LayerManager.cs +++ /dev/null @@ -1,826 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using Point = System.Drawing.Point; -using Rectangle = System.Drawing.Rectangle; - -namespace UVtools.Parser -{ - #region LayerIssue Class - - public class LayerIssue : IEnumerable<Point> - { - public enum IssueType : byte - { - Island, - ResinTrap, - TouchingBound, - //HoleSandwich, - } - - /// <summary> - /// Gets the parent layer - /// </summary> - public Layer Layer { get; } - - /// <summary> - /// Gets the issue type associated - /// </summary> - public IssueType Type { get; } - - /// <summary> - /// Gets the pixels containing the issue - /// </summary> - public Point[] Pixels { get; } - - /// <summary> - /// Gets the bounding rectangle of the pixel area - /// </summary> - public Rectangle BoundingRectangle { get; } - - /// <summary> - /// Gets the X coordinate for the first point, -1 if doesn't exists - /// </summary> - public int X => HaveValidPoint ? Pixels[0].X : -1; - - /// <summary> - /// Gets the Y coordinate for the first point, -1 if doesn't exists - /// </summary> - public int Y => HaveValidPoint ? Pixels[0].Y : -1; - - /// <summary> - /// Gets the XY point for first point - /// </summary> - public Point Point => HaveValidPoint ? Pixels[0] : new Point(-1, -1); - - /// <summary> - /// Gets the number of pixels on this issue - /// </summary> - public uint Size { - get - { - if (Type == IssueType.ResinTrap && !BoundingRectangle.IsEmpty) - { - return (uint) (BoundingRectangle.Width * BoundingRectangle.Height); - } - - if (ReferenceEquals(Pixels, null)) return 0; - return (uint) Pixels.Length; - } - } - - /// <summary> - /// Check if this issue have a valid start point to show - /// </summary> - public bool HaveValidPoint => !ReferenceEquals(Pixels, null) && Pixels.Length > 0; - - public LayerIssue(Layer layer, IssueType type, Point[] pixels = null, Rectangle boundingRectangle = new Rectangle()) - { - Layer = layer; - Type = type; - Pixels = pixels; - BoundingRectangle = boundingRectangle; - } - - public Point this[uint index] => Pixels[index]; - - public Point this[int index] => Pixels[index]; - - public IEnumerator<Point> GetEnumerator() - { - return ((IEnumerable<Point>)Pixels).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public override string ToString() - { - return $"{nameof(Type)}: {Type}"; - } - } - #endregion - - #region LayerHollowArea - - public class LayerHollowArea : IEnumerable<Point> - { - public enum AreaType : byte - { - Unknown = 0, - Trap, - Drain - } - /// <summary> - /// Gets area pixels - /// </summary> - public Point[] Contour { get; } - - public System.Drawing.Rectangle BoundingRectangle { get; } - - public AreaType Type { get; set; } = AreaType.Unknown; - - public bool Processed { get; set; } - - #region Indexers - public Point this[uint index] - { - get => index < Contour.Length ? Contour[index] : Point.Empty; - set => Contour[index] = value; - } - - public Point this[int index] - { - get => index < Contour.Length ? Contour[index] : Point.Empty; - set => Contour[index] = value; - } - - public Point this[uint x, uint y] - { - get - { - for (uint i = 0; i < Contour.Length; i++) - { - if (Contour[i].X == x && Contour[i].Y == y) return Contour[i]; - } - return Point.Empty; - } - } - - public Point this[int x, int y] => this[(uint) x, (uint)y]; - - public Point this[Point point] => this[point.X, point.Y]; - - #endregion - - public IEnumerator<Point> GetEnumerator() - { - return ((IEnumerable<Point>)Contour).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public LayerHollowArea() - { - } - - public LayerHollowArea(Point[] contour, System.Drawing.Rectangle boundingRectangle, AreaType type = AreaType.Unknown) - { - Contour = contour; - BoundingRectangle = boundingRectangle; - Type = type; - } - } - #endregion - - #region Layer Class - /// <summary> - /// Represent a Layer - /// </summary> - public class Layer : IEquatable<Layer>, IEquatable<uint> - { - #region Properties - - /// <summary> - /// Gets the parent layer manager - /// </summary> - public LayerManager ParentLayerManager { get; set; } - - /// <summary> - /// Gets the layer index - /// </summary> - public uint Index { get; } - - private byte[] _rawData; - /// <summary> - /// Gets or sets layer image compressed data - /// </summary> - public byte[] RawData - { - get => LayerManager.DecompressLayer(_rawData); - set - { - _rawData = LayerManager.CompressLayer(value); - IsModified = true; - } - } - - /// <summary> - /// Gets the original filename, null if no filename attached with layer - /// </summary> - public string Filename { get; set; } - - /// <summary> - /// Gets if layer has been modified - /// </summary> - public bool IsModified { get; set; } - - /// <summary> - /// Gets or sets a new image instance - /// </summary> - public Image<L8> Image - { - get => SixLabors.ImageSharp.Image.Load<L8>(RawData); - set - { - using (MemoryStream stream = new MemoryStream()) - { - value.Save(stream, Helpers.PngEncoder); - RawData = stream.ToArray(); - } - } - } - - /// <summary> - /// Gets a new RGBA image instance - /// </summary> - public Image<Rgba32> ImageRbga32 => Image.CloneAs<Rgba32>(); - - #endregion - - #region Constructor - public Layer(uint index, byte[] rawData, string filename = null, LayerManager pararentLayerManager = null) - { - Index = index; - RawData = rawData; - Filename = filename ?? $"Layer{index}.png"; - IsModified = false; - ParentLayerManager = pararentLayerManager; - } - - public Layer(uint index, Image<L8> image, string filename = null, LayerManager pararentLayerManager = null) : this(index, new byte[0], filename, pararentLayerManager) - { - Image = image; - IsModified = false; - } - - - public Layer(uint index, Stream stream, string filename = null, LayerManager pararentLayerManager = null) : this(index, stream.ToArray(), filename, pararentLayerManager) - { } - #endregion - - #region Equatables - - public static bool operator ==(Layer obj1, Layer obj2) - { - return obj1.Equals(obj2); - } - - public static bool operator !=(Layer obj1, Layer obj2) - { - return !obj1.Equals(obj2); - } - - public static bool operator >(Layer obj1, Layer obj2) - { - return obj1.Index > obj2.Index; - } - - public static bool operator <(Layer obj1, Layer obj2) - { - return obj1.Index < obj2.Index; - } - - public static bool operator >=(Layer obj1, Layer obj2) - { - return obj1.Index >= obj2.Index; - } - - public static bool operator <=(Layer obj1, Layer obj2) - { - return obj1.Index <= obj2.Index; - } - - public bool Equals(uint other) - { - return Index == other; - } - - public bool Equals(Layer other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(_rawData, other._rawData); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Layer)obj); - } - - public override int GetHashCode() - { - return (_rawData != null ? _rawData.GetHashCode() : 0); - } - - private sealed class IndexRelationalComparer : IComparer<Layer> - { - public int Compare(Layer x, Layer y) - { - if (ReferenceEquals(x, y)) return 0; - if (ReferenceEquals(null, y)) return 1; - if (ReferenceEquals(null, x)) return -1; - return x.Index.CompareTo(y.Index); - } - } - - public static IComparer<Layer> IndexComparer { get; } = new IndexRelationalComparer(); - #endregion - - #region Formaters - public override string ToString() - { - return $"{nameof(Index)}: {Index}, {nameof(Filename)}: {Filename}, {nameof(IsModified)}: {IsModified}"; - } - #endregion - - #region Methods - - public Layer PreviousLayer() - { - if (ReferenceEquals(ParentLayerManager, null) || Index == 0) - return null; - - return ParentLayerManager[Index - 1]; - } - - public Layer NextLayer() - { - if (ReferenceEquals(ParentLayerManager, null) || Index >= ParentLayerManager.Count - 1) - return null; - - return ParentLayerManager[Index + 1]; - } - - /// <summary> - /// Gets all islands start pixel location for this layer - /// https://www.geeksforgeeks.org/find-number-of-islands/ - /// </summary> - /// <returns><see cref="List{T}"/> holding all islands coordinates</returns> - public List<LayerIssue> GetIssues(uint requiredPixelsToSupportIsland = 5) - { - if (requiredPixelsToSupportIsland == 0) - requiredPixelsToSupportIsland = 1; - - // These arrays are used to - // get row and column numbers - // of 8 neighbors of a given cell - List<LayerIssue> result = new List<LayerIssue>(); - List<Point> pixels = new List<Point>(); - - - - var image = Image; - byte[] bytes = null; - if (image.TryGetSinglePixelSpan(out var pixelSpan)) - { - bytes = MemoryMarshal.AsBytes(pixelSpan).ToArray(); - } - - var previousLayerImage = PreviousLayer()?.Image; - byte[] previousBytes = null; - if (!ReferenceEquals(previousLayerImage, null)) - { - if (previousLayerImage.TryGetSinglePixelSpan(out var previousPixelSpan)) - { - previousBytes = MemoryMarshal.AsBytes(previousPixelSpan).ToArray(); - } - } - - /*var nextLayerImage = NextLayer()?.Image; - byte[] nextBytes = null; - if (!ReferenceEquals(nextLayerImage, null)) - { - if (nextLayerImage.TryGetSinglePixelSpan(out var nextPixelSpan)) - { - nextBytes = MemoryMarshal.AsBytes(nextPixelSpan).ToArray(); - } - }*/ - - // Make a bool array to - // mark visited cells. - // Initially all cells - // are unvisited - bool[,] visited = new bool[image.Width, image.Height]; - - // Initialize count as 0 and - // traverse through the all - // cells of given matrix - //uint count = 0; - - // Island checker - sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 }; - sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 }; - const uint minPixel = 10; - const uint minPixelForSupportIsland = 200; - int pixelIndex; - uint islandSupportingPixels; - if (Index > 0) - { - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - pixelIndex = y * image.Width + x; - - /*if (bytes[pixelIndex] == 0 && previousBytes?[pixelIndex] == byte.MaxValue && - nextBytes?[pixelIndex] == byte.MaxValue) - { - result.Add(new LayerIssue(this, LayerIssue.IssueType.HoleSandwich, new []{new Point(x, y)})); - }*/ - - if (bytes[pixelIndex] > minPixel && !visited[x, y]) - { - // If a cell with value 1 is not - // visited yet, then new island - // found, Visit all cells in this - // island and increment island count - pixels.Clear(); - pixels.Add(new Point(x, y)); - islandSupportingPixels = previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0; - - int minX = x; - int maxX = x; - int minY = y; - int maxY = y; - - int x2; - int y2; - - - Queue<Point> queue = new Queue<Point>(); - queue.Enqueue(new Point(x, y)); - // Mark this cell as visited - visited[x, y] = true; - - while (queue.Count > 0) - { - var point = queue.Dequeue(); - y2 = point.Y; - x2 = point.X; - for (byte k = 0; k < 8; k++) - { - //if (isSafe(y2 + rowNbr[k], x2 + colNbr[k])) - var tempy2 = y2 + rowNbr[k]; - var tempx2 = x2 + colNbr[k]; - pixelIndex = tempy2 * image.Width + tempx2; - if (tempy2 >= 0 && - tempy2 < image.Height && - tempx2 >= 0 && tempx2 < image.Width && - bytes[pixelIndex] >= minPixel && - !visited[tempx2, tempy2]) - { - visited[tempx2, tempy2] = true; - point = new Point(tempx2, tempy2); - pixels.Add(point); - queue.Enqueue(point); - - minX = Math.Min(minX, tempx2); - maxX = Math.Max(maxX, tempx2); - minY = Math.Min(minY, tempy2); - maxY = Math.Max(maxY, tempy2); - - islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0; - } - } - } - //count++; - - if (islandSupportingPixels >= requiredPixelsToSupportIsland) - continue; // Not a island, bounding is strong - if (islandSupportingPixels > 0 && pixels.Count < requiredPixelsToSupportIsland && - islandSupportingPixels >= Math.Max(1, pixels.Count / 2)) continue; // Not a island - result.Add(new LayerIssue(this, LayerIssue.IssueType.Island, pixels.ToArray(), new Rectangle(minX, minY, maxX-minX, maxY-minY))); - } - } - } - } - - pixels.Clear(); - - // TouchingBounds Checker - for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds - { - if (bytes[x] >= 200) // Top - { - pixels.Add(new Point(x, 0)); - } - - if (bytes[image.Width * image.Height - image.Width + x] >= 200) // Bottom - { - pixels.Add(new Point(x, image.Height-1)); - } - } - - for (int y = 0; y < image.Height; y++) // Check Left and Right bounds - { - if (bytes[y * image.Width] >= 200) // Left - { - pixels.Add(new Point(0, y)); - } - - if (bytes[y * image.Width + image.Width - 1] >= 200) // Right - { - pixels.Add(new Point(image.Width-1, y)); - } - } - - if (pixels.Count > 0) - { - result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray())); - } - - pixels.Clear(); - - return result; - } - - public Layer Clone() - { - return new Layer(Index, RawData, Filename, ParentLayerManager); - } - #endregion - } - #endregion - - #region LayerManager Class - public class LayerManager : IEnumerable<Layer> - { - #region Properties - /// <summary> - /// Layers List - /// </summary> - public Layer[] Layers { get; } - - /// <summary> - /// Gets the layers count - /// </summary> - public uint Count => (uint) Layers.Length; - - /// <summary> - /// Gets if any layer got modified, otherwise false - /// </summary> - public bool IsModified - { - get - { - for (uint i = 0; i < Count; i++) - { - if (Layers[i].IsModified) return true; - } - return false; - } - } - - - #endregion - - #region Constructors - public LayerManager(uint layerCount) - { - Layers = new Layer[layerCount]; - } - #endregion - - #region Indexers - public Layer this[uint index] - { - get => Layers[index]; - set => AddLayer(index, value); - } - - public Layer this[int index] - { - get => Layers[index]; - set => AddLayer((uint) index, value); - } - - public Layer this[long index] - { - get => Layers[index]; - set => AddLayer((uint) index, value); - } - - #endregion - - #region Numerators - public IEnumerator<Layer> GetEnumerator() - { - return ((IEnumerable<Layer>)Layers).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion - - #region Static Methods - /// <summary> - /// Compress a layer from a <see cref="Stream"/> - /// </summary> - /// <param name="input"><see cref="Stream"/> to compress</param> - /// <returns>Compressed byte array</returns> - public static byte[] CompressLayer(Stream input) - { - return CompressLayer(input.ToArray()); - } - - /// <summary> - /// Compress a layer from a byte array - /// </summary> - /// <param name="input">byte array to compress</param> - /// <returns>Compressed byte array</returns> - public static byte[] CompressLayer(byte[] input) - { - return input; - /*using (MemoryStream output = new MemoryStream()) - { - using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal)) - { - dstream.Write(input, 0, input.Length); - } - return output.ToArray(); - }*/ - } - - /// <summary> - /// Decompress a layer from a byte array - /// </summary> - /// <param name="input">byte array to decompress</param> - /// <returns>Decompressed byte array</returns> - public static byte[] DecompressLayer(byte[] input) - { - return input; - /*using (MemoryStream ms = new MemoryStream(input)) - { - using (MemoryStream output = new MemoryStream()) - { - using (DeflateStream dstream = new DeflateStream(ms, CompressionMode.Decompress)) - { - dstream.CopyTo(output); - } - return output.ToArray(); - } - }*/ - } - #endregion - - #region Methods - - /// <summary> - /// Add a layer - /// </summary> - /// <param name="index">Layer index</param> - /// <param name="layer">Layer to add</param> - public void AddLayer(uint index, Layer layer) - { - Layers[index] = layer; - layer.ParentLayerManager = this; - } - - /// <summary> - /// Get layer given index - /// </summary> - /// <param name="index">Layer index</param> - /// <returns></returns> - public Layer GetLayer(uint index) - { - return Layers[index]; - } - - public ConcurrentDictionary<uint, List<LayerIssue>> GetAllIssues() - { - var result = new ConcurrentDictionary<uint, List<LayerIssue>>(); - - Parallel.ForEach(this, layer => - { - var issues = layer.GetIssues(); - if (issues.Count > 0) - { - if (!result.TryAdd(layer.Index, issues)) - { - throw new AccessViolationException("Error while trying to add an issue to the dictionary, please try again."); - } - } - }); - - /*const byte minPixel = 50; - sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 }; - sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 }; - - int pixelIndex; - for (uint layerindex = 0; layerindex < Count; layerindex++) - { - var image = this[layerindex].Image; - byte[] bytes = null; - if (image.TryGetSinglePixelSpan(out var pixelSpan)) - { - bytes = MemoryMarshal.AsBytes(pixelSpan).ToArray(); - } - - bool[,] visited = new bool[image.Width, image.Height]; - - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - pixelIndex = y * image.Width + x; - if (bytes[pixelIndex] > minPixel && !visited[y, x]) - { - Queue<Point> queue = new Queue<Point>(); - queue.Enqueue(new Point(x, y)); - // Mark this cell as visited - visited[x, y] = true; - - var x2 = x; - var y2 = y; - - while (queue.Count > 0) - { - var point = queue.Dequeue(); - y2 = point.Y; - x2 = point.X; - for (byte k = 0; k < 8; k++) - { - //if (isSafe(y2 + rowNbr[k], x2 + colNbr[k])) - var tempy2 = y2 + rowNbr[k]; - var tempx2 = x2 + colNbr[k]; - pixelIndex = tempy2 * image.Width + tempx2; - if (tempy2 >= 0 && - tempy2 < image.Height && - tempx2 >= 0 && tempx2 < image.Width && - bytes[pixelIndex] >= minPixel && - !visited[tempx2, tempy2]) - { - visited[tempx2, tempy2] = true; - point = new Point(tempx2, tempy2); - pixels.Add(point); - queue.Enqueue(point); - - islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0; - } - } - } - } - } - } - }*/ - - return result; - } - - /// <summary> - /// Desmodify all layers - /// </summary> - public void Desmodify() - { - for (uint i = 0; i < Count; i++) - { - Layers[i].IsModified = false; - } - } - - /// <summary> - /// Clone this object - /// </summary> - /// <returns></returns> - public LayerManager Clone() - { - LayerManager layerManager = new LayerManager(Count); - foreach (var layer in this) - { - layerManager[layer.Index] = layer.Clone(); - } - - return layerManager; - } - - - #endregion - - } - #endregion -} diff --git a/UVtools.Parser/PHZFile.cs b/UVtools.Parser/PHZFile.cs deleted file mode 100644 index c14efbe..0000000 --- a/UVtools.Parser/PHZFile.cs +++ /dev/null @@ -1,1329 +0,0 @@ -/* - * 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. - */ - -// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using BinarySerialization; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using UVtools.Parser; - -namespace UVtools.Parser -{ - public class PHZFile : FileFormat - { - #region Constants - private const uint MAGIC_PHZ = 0x9FDA83AE; - private const ushort REPEATRGB15MASK = 0x20; - - private const ushort RLE16EncodingLimit = 0x1000; - #endregion - - #region Sub Classes - #region Header - public class Header - { - - /// <summary> - /// Gets a magic number identifying the file type. - /// 0x12fd_0019 for cbddlp - /// 0x12fd_0086 for ctb - /// 0x9FDA83AE for phz - /// </summary> - [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_PHZ; - - /// <summary> - /// Gets the software version - /// </summary> - [FieldOrder(1)] public uint Version { get; set; } = 2; - - /// <summary> - /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table. - /// </summary> - [FieldOrder(2)] public float LayerHeightMilimeter { get; set; } - - /// <summary> - /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table. - /// </summary> - [FieldOrder(3)] public float LayerExposureSeconds { get; set; } - - /// <summary> - /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table. - /// </summary> - [FieldOrder(4)] public float BottomExposureSeconds { get; set; } - - /// <summary> - /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.. - /// </summary> - [FieldOrder(5)] public uint BottomLayersCount { get; set; } = 10; - - /// <summary> - /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images. - /// </summary> - [FieldOrder(6)] public uint ResolutionX { get; set; } - - /// <summary> - /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images. - /// </summary> - [FieldOrder(7)] public uint ResolutionY { get; set; } - - /// <summary> - /// Gets the file offsets of ImageHeader records describing the larger preview images. - /// </summary> - [FieldOrder(8)] public uint PreviewLargeOffsetAddress { get; set; } - - /// <summary> - /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer. - /// </summary> - [FieldOrder(9)] public uint LayersDefinitionOffsetAddress { get; set; } - - /// <summary> - /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files. - /// </summary> - [FieldOrder(10)] public uint LayerCount { get; set; } - - /// <summary> - /// Gets the file offsets of ImageHeader records describing the smaller preview images. - /// </summary> - [FieldOrder(11)] public uint PreviewSmallOffsetAddress { get; set; } - - /// <summary> - /// Gets the estimated duration of print, in seconds. - /// </summary> - [FieldOrder(12)] public uint PrintTime { get; set; } - - /// <summary> - /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose. - /// </summary> - [FieldOrder(13)] public uint ProjectorType { get; set; } - - /// <summary> - /// Gets the number of times each layer image is repeated in the file. - /// This is used to implement antialiasing in cbddlp files. When greater than 1, - /// the layer table will actually contain layer_table_count * level_set_count entries. - /// See the section on antialiasing for details. - /// </summary> - [FieldOrder(14)] public uint AntiAliasLevel { get; set; } = 1; - - /// <summary> - /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively. - /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off. - /// </summary> - [FieldOrder(15)] public ushort LightPWM { get; set; } = 255; - - /// <summary> - /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively. - /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off. - /// </summary> - [FieldOrder(16)] public ushort BottomLightPWM { get; set; } = 255; - - [FieldOrder(17)] public uint Padding1 { get; set; } - [FieldOrder(18)] public uint Padding2 { get; set; } - - /// <summary> - /// Gets the height of the model described by this file, in millimeters. - /// </summary> - [FieldOrder(19)] public float OverallHeightMilimeter { get; set; } - - /// <summary> - /// Gets dimensions of the printer’s X output volume, in millimeters. - /// </summary> - [FieldOrder(20)] public float BedSizeX { get; set; } - - /// <summary> - /// Gets dimensions of the printer’s Y output volume, in millimeters. - /// </summary> - [FieldOrder(21)] public float BedSizeY { get; set; } - - /// <summary> - /// Gets dimensions of the printer’s Z output volume, in millimeters. - /// </summary> - [FieldOrder(22)] public float BedSizeZ { get; set; } - - /// <summary> - /// Gets the key used to encrypt layer data, or 0 if encryption is not used. - /// </summary> - [FieldOrder(23)] public uint EncryptionKey { get; set; } - - /// <summary> - /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(24)] public float BottomLightOffDelay { get; set; } = 1; - - /// <summary> - /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(25)] public float LayerOffTime { get; set; } = 1; - - /// <summary> - /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig. - /// </summary> - [FieldOrder(26)] public uint BottomLayerCount { get; set; } = 10; - - [FieldOrder(27)] public uint Padding3 { get; set; } - - /// <summary> - /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters. - /// </summary> - [FieldOrder(28)] public float BottomLiftHeight { get; set; } = 5; - - /// <summary> - /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute. - /// </summary> - [FieldOrder(29)] public float BottomLiftSpeed { get; set; } = 300; - - /// <summary> - /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters. - /// </summary> - [FieldOrder(30)] public float LiftHeight { get; set; } = 5; - - /// <summary> - /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute. - /// </summary> - [FieldOrder(31)] public float LiftSpeed { get; set; } = 300; - - /// <summary> - /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute. - /// </summary> - [FieldOrder(32)] public float RetractSpeed { get; set; } = 300; - - /// <summary> - /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model. - /// </summary> - [FieldOrder(33)] public float VolumeMl { get; set; } - - /// <summary> - /// Gets the estimated grams, derived from volume using configured factors for density. - /// </summary> - [FieldOrder(34)] public float WeightG { get; set; } - - /// <summary> - /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost. - /// </summary> - [FieldOrder(35)] public float CostDollars { get; set; } - - [FieldOrder(36)] public uint Padding4 { get; set; } - - /// <summary> - /// Gets the machine name offset to a string naming the machine type, and its length in bytes. - /// </summary> - [FieldOrder(37)] public uint MachineNameAddress { get; set; } - - /// <summary> - /// Gets the machine size in bytes - /// </summary> - [FieldOrder(38)] public uint MachineNameSize { get; set; } - - /// <summary> - /// Gets the machine name. string is not nul-terminated. - /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only. - /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine. - /// </summary> - [Ignore] public string MachineName { get; set; } - - [FieldOrder(39)] public uint Padding5 { get; set; } - [FieldOrder(40)] public uint Padding6 { get; set; } - [FieldOrder(41)] public uint Padding7 { get; set; } - [FieldOrder(42)] public uint Padding8 { get; set; } - [FieldOrder(43)] public uint Padding9 { get; set; } - [FieldOrder(44)] public uint Padding10 { get; set; } - - /// <summary> - /// Gets the parameter used to control encryption. - /// Not totally understood. 0 for cbddlp files, 0xF for ctb files, 0x1c for phz - /// </summary> - [FieldOrder(45)] public uint EncryptionMode { get; set; } = 28; - - /// <summary> - /// Gets a number that increments with time or number of models sliced, or both. Zeroing it in output seems to have no effect. Possibly a user tracking bug. - /// </summary> - [FieldOrder(46)] public uint MysteriousId { get; set; } - - /// <summary> - /// Gets a number that increments with time or number of models sliced, or both. Zeroing it in output seems to have no effect. Possibly a user tracking bug. - /// </summary> - [FieldOrder(47)] public uint AntiAliasLevelInfo { get; set; } - - [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300; - - [FieldOrder(49)] public uint Padding11 { get; set; } - [FieldOrder(50)] public uint Padding12 { get; set; } - [FieldOrder(51)] public uint Padding13 { get; set; } - [FieldOrder(52)] public uint Padding14 { get; set; } - [FieldOrder(53)] public uint Padding15 { get; set; } - [FieldOrder(54)] public uint Padding16{ get; set; } - - public override string ToString() - { - return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LayerOffTime)}: {LayerOffTime}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(Padding3)}: {Padding3}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(Padding4)}: {Padding4}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevelInfo)}: {AntiAliasLevelInfo}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding11)}: {Padding11}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}"; - } - } - #endregion - - #region Preview - /// <summary> - /// The files contain two preview images. - /// These are shown on the printer display when choosing which file to print, sparing the poor printer from needing to render a 3D image from scratch. - /// </summary> - public class Preview - { - /// <summary> - /// Gets the X dimension of the preview image, in pixels. - /// </summary> - [FieldOrder(0)] public uint ResolutionX { get; set; } - - /// <summary> - /// Gets the Y dimension of the preview image, in pixels. - /// </summary> - [FieldOrder(1)] public uint ResolutionY { get; set; } - - /// <summary> - /// Gets the image offset of the encoded data blob. - /// </summary> - [FieldOrder(2)] public uint ImageOffset { get; set; } - - /// <summary> - /// Gets the image length in bytes. - /// </summary> - [FieldOrder(3)] public uint ImageLength { get; set; } - - [FieldOrder(4)] public uint Unknown1 { get; set; } - [FieldOrder(5)] public uint Unknown2 { get; set; } - [FieldOrder(6)] public uint Unknown3 { get; set; } - [FieldOrder(7)] public uint Unknown4 { get; set; } - - public Image<Rgba32> Decode(byte[] rawImageData) - { - var image = new Image<Rgba32>((int)ResolutionX, (int)ResolutionY); - image.TryGetSinglePixelSpan(out var span); - int pixel = 0; - for (uint n = 0; n < ImageLength; n++) - { - uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8)); - //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3); - byte red = (byte)(((dot >> 11) & 0x1F) << 3); - byte green = (byte)(((dot >> 6) & 0x1F) << 3); - byte blue = (byte)((dot & 0x1F) << 3); - int repeat = 1; - if ((dot & 0x0020) == 0x0020) - { - repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8); - } - - - for (int j = 0; j < repeat; j++) - { - span[pixel] = new Rgba32(red, green, blue, byte.MaxValue); - pixel++; - } - } - - return image; - } - - public static byte[] Encode(Image<Rgba32> image) - { - List<byte> rawData = new List<byte>(); - ushort color15 = 0; - uint rep = 0; - - void RleRGB15() - { - switch (rep) - { - case 0: - return; - case 1: - rawData.Add((byte)(color15 & ~REPEATRGB15MASK)); - rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8)); - break; - case 2: - for (int i = 0; i < 2; i++) - { - rawData.Add((byte)(color15 & ~REPEATRGB15MASK)); - rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8)); - } - - break; - default: - rawData.Add((byte)(color15 | REPEATRGB15MASK)); - rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8)); - rawData.Add((byte)((rep - 1) | 0x3000)); - rawData.Add((byte)(((rep - 1) | 0x3000) >> 8)); - break; - } - } - - for (int y = 0; y < image.Height; y++) - { - Span<Rgba32> pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var ncolor15 = - (pixelRowSpan[x].B >> 3) - | ((pixelRowSpan[x].G >> 2) << 5) - | ((pixelRowSpan[x].R >> 3) << 11); - - if (ncolor15 == color15) - { - rep++; - if (rep == RLE16EncodingLimit) - { - RleRGB15(); - rep = 0; - } - } - else - { - RleRGB15(); - color15 = (ushort)ncolor15; - rep = 1; - } - } - } - - RleRGB15(); - - return rawData.ToArray(); - } - - public override string ToString() - { - return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; - } - } - - #endregion - - #region Layer - public class LayerData - { - /// <summary> - /// Gets the build platform Z position for this layer, measured in millimeters. - /// </summary> - [FieldOrder(0)] public float LayerPositionZ { get; set; } - - /// <summary> - /// Gets the exposure time for this layer, in seconds. - /// </summary> - [FieldOrder(1)] public float LayerExposure { get; set; } - - /// <summary> - /// Gets how long to keep the light off after exposing this layer, in seconds. - /// </summary> - [FieldOrder(2)] public float LayerOffTimeSeconds { get; set; } - - /// <summary> - /// Gets the layer image offset to encoded layer data, and its length in bytes. - /// </summary> - [FieldOrder(3)] public uint DataAddress { get; set; } - - /// <summary> - /// Gets the layer image length in bytes. - /// </summary> - [FieldOrder(4)] public uint DataSize { get; set; } - [FieldOrder(5)] public uint Unknown1 { get; set; } - [FieldOrder(6)] public uint Unknown2 { get; set; } - [FieldOrder(7)] public uint Unknown3 { get; set; } - [FieldOrder(8)] public uint Unknown4 { get; set; } - - [Ignore] public byte[] EncodedRle { get; set; } - - [Ignore] public PHZFile Parent { get; set; } - - public LayerData() - { - } - - public LayerData(PHZFile parent, uint layerIndex) - { - Parent = parent; - LayerOffTimeSeconds = layerIndex < parent.HeaderSettings.BottomLayersCount ? parent.HeaderSettings.BottomLightOffDelay : parent.HeaderSettings.LayerOffTime; - LayerExposure = layerIndex < Parent.InitialLayerCount ? Parent.HeaderSettings.BottomExposureSeconds : Parent.HeaderSettings.LayerExposureSeconds; - LayerPositionZ = Parent.GetHeightFromLayer(layerIndex); - } - - public Image<L8> Decode(uint layerIndex, bool consumeData = true) - { - Image<L8> image = new Image<L8>((int)Parent.ResolutionX, (int)Parent.ResolutionY); - - - if (Parent.HeaderSettings.EncryptionKey > 0) - { - KeyRing kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex); - EncodedRle = kr.Read(EncodedRle); - } - - int limit = image.Width * image.Height; - int index = 0; - byte lastColor = 0; - - image.TryGetSinglePixelSpan(out var span); - - foreach (var code in EncodedRle) - { - if ((code & 0x80) == 0x80) - { - //lastColor = (byte) (code << 1); - // // Convert from 7bpp to 8bpp (extending the last bit) - lastColor = (byte)(((code & 0x7f) << 1) | (code & 1)); - if (lastColor >= 0xfc) - { - // Make 'white' actually white - lastColor = 0xff; - - } - - if (index < limit) - { - span[index].PackedValue = lastColor; - } - else - { - Debug.WriteLine("Corrupted RLE data."); - } - - index++; - } - else - { - for (uint i = 0; i < code; i++) - { - if (index < limit) - { - span[index].PackedValue = lastColor; - } - else - { - Debug.WriteLine("Corrupted RLE data."); - } - index++; - } - } - } - - if (consumeData) - EncodedRle = null; - - return image; - } - - public void Encode(Image<L8> image, uint layerIndex) - { - List<byte> rawData = new List<byte>(); - //byte color = byte.MaxValue >> 1; - byte color = byte.MaxValue; - uint stride = 0; - - void AddRep() - { - rawData.Add((byte)(color | 0x80)); - stride--; - int done = 0; - while (done < stride) - { - int todo = 0x7d; - - if (stride - done < todo) - { - todo = (int)(stride - done); - } - - rawData.Add((byte)(todo)); - - done += todo; - } - } - - int halfWidth = image.Width / 2; - - for (int y = 0; y < image.Height; y++) - { - var pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var grey7 = (byte)((pixelRowSpan[x].PackedValue >> 1) & 0x7f); - if (grey7 > 0x7c) - { - grey7 = 0x7c; - } - - if (color == byte.MaxValue) - { - color = grey7; - stride = 1; - } - else if (grey7 != color || x == halfWidth) - { - AddRep(); - color = grey7; - stride = 1; - } - else - { - stride++; - } - } - - AddRep(); - color = byte.MaxValue; - } - - - if (Parent.HeaderSettings.EncryptionKey > 0) - { - KeyRing kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex); - EncodedRle = kr.Read(rawData).ToArray(); - } - else - { - EncodedRle = rawData.ToArray(); - } - - DataSize = (uint) EncodedRle.Length; - } - - public override string ToString() - { - return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LayerOffTimeSeconds)}: {LayerOffTimeSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; - } - } - #endregion - - #region KeyRing - - public class KeyRing - { - public uint Init { get; } - public uint Key { get; private set; } - public uint Index { get; private set; } - - public KeyRing(uint seed, uint layerIndex) - { - seed %= 0x4324; - Init = seed * 0x34a32231; - Key = (layerIndex ^ 0x3fad2212) * seed * 0x4910913d; - } - - public byte Next() - { - byte k = (byte)(Key >> (int)(8 * Index)); - Index++; - - if ((Index & 3) == 0) - { - Key += Init; - Index = 0; - } - - return k; - } - - public List<byte> Read(List<byte> input) - { - List<byte> data = new List<byte>(input.Count); - data.AddRange(input.Select(t => (byte)(t ^ Next()))); - - return data; - } - - public byte[] Read(byte[] input) - { - byte[] data = new byte[input.Length]; - for (int i = 0; i < input.Length; i++) - { - data[i] = (byte) (input[i] ^ Next()); - } - return data; - } - } - - #endregion - - #endregion - - #region Properties - - public Header HeaderSettings { get; protected internal set; } = new Header(); - - public Preview[] Previews { get; protected internal set; } - - public LayerData[] LayersDefinitions { get; private set; } - - public Dictionary<string, LayerData> LayersHash { get; } = new Dictionary<string, LayerData>(); - - public override FileFormatType FileType => FileFormatType.Binary; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("phz", "Chitubox PHZ Files"), - }; - - public override Type[] ConvertToFormats { get; } = - { - typeof(ChituboxFile), - typeof(ChituboxZipFile), - typeof(PWSFile), - typeof(ZCodexFile), - }; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = - { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - PrintParameterModifier.BottomLayerOffTime, - PrintParameterModifier.LayerOffTime, - PrintParameterModifier.BottomLiftHeight, - PrintParameterModifier.BottomLiftSpeed, - PrintParameterModifier.LiftHeight, - PrintParameterModifier.LiftSpeed, - PrintParameterModifier.RetractSpeed, - - PrintParameterModifier.BottomLightPWM, - PrintParameterModifier.LightPWM, - }; - - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)}; - - public override uint ResolutionX => HeaderSettings.ResolutionX; - - public override uint ResolutionY => HeaderSettings.ResolutionY; - public override byte AntiAliasing => (byte) HeaderSettings.AntiAliasLevelInfo; - - public override float LayerHeight => HeaderSettings.LayerHeightMilimeter; - - public override ushort InitialLayerCount => (ushort)HeaderSettings.BottomLayersCount; - - public override float InitialExposureTime => HeaderSettings.BottomExposureSeconds; - - public override float LayerExposureTime => HeaderSettings.LayerExposureSeconds; - public override float LiftHeight => HeaderSettings.LiftHeight; - public override float LiftSpeed => HeaderSettings.LiftSpeed; - public override float RetractSpeed => HeaderSettings.RetractSpeed; - - public override float PrintTime => HeaderSettings.PrintTime; - - public override float UsedMaterial => (float) Math.Round(HeaderSettings.VolumeMl, 2); - - public override float MaterialCost => (float) Math.Round(HeaderSettings.CostDollars, 2); - - public override string MaterialName => "Unknown"; - public override string MachineName => HeaderSettings.MachineName; - - public override object[] Configs => new object[] { HeaderSettings }; - - #endregion - - #region Constructors - public PHZFile() - { - Previews = new Preview[ThumbnailsCount]; - } - #endregion - - #region Methods - public override void Clear() - { - base.Clear(); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - Previews[i] = new Preview(); - } - - LayersDefinitions = null; - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - LayersHash.Clear(); - - /*if (HeaderSettings.EncryptionKey == 0) - { - Random rnd = new Random(); - HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue); - }*/ - - - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - var image = Thumbnails[i]; - - var bytes = Preview.Encode(image); - - if (bytes.Length == 0) continue; - - if (i == (byte) FileThumbnailSize.Small) - { - HeaderSettings.PreviewSmallOffsetAddress = currentOffset; - } - else - { - HeaderSettings.PreviewLargeOffsetAddress = currentOffset; - } - - - - Preview preview = new Preview - { - ResolutionX = (uint) image.Width, - ResolutionY = (uint) image.Height, - ImageLength = (uint)bytes.Length, - }; - - currentOffset += (uint) Helpers.Serializer.SizeOf(preview); - preview.ImageOffset = currentOffset; - - Helpers.SerializeWriteFileStream(outputFile, preview); - - currentOffset += (uint)bytes.Length; - outputFile.WriteBytes(bytes); - } - - if (HeaderSettings.MachineNameSize > 0) - { - HeaderSettings.MachineNameAddress = currentOffset; - var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName); - outputFile.Write(machineBytes, 0, machineBytes.Length); - currentOffset += (uint)machineBytes.Length; - } - - Parallel.For(0, LayerCount, layerIndex => - { - LayerData layer = new LayerData(this, (uint)layerIndex); - layer.Encode(this[layerIndex].Image, (uint) layerIndex); - LayersDefinitions[layerIndex] = layer; - }); - - HeaderSettings.LayersDefinitionOffsetAddress = currentOffset; - uint layerDataCurrentOffset = currentOffset + (uint) Helpers.Serializer.SizeOf(LayersDefinitions[0]) * LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - LayerData layerData = LayersDefinitions[layerIndex]; - LayerData layerDataHash = null; - - if (HeaderSettings.EncryptionKey == 0) - { - string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle); - if (LayersHash.TryGetValue(hash, out layerDataHash)) - { - layerData.DataAddress = layerDataHash.DataAddress; - layerData.DataSize = layerDataHash.DataSize; - } - else - { - LayersHash.Add(hash, layerData); - } - } - - if (ReferenceEquals(layerDataHash, null)) - { - layerData.DataAddress = layerDataCurrentOffset; - - outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); - layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle); - } - - - LayersDefinitions[layerIndex] = layerData; - - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layerData); - } - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - outputFile.Close(); - outputFile.Dispose(); - - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(Previews[0]); - Debug.WriteLine(Previews[1]); - Debug.WriteLine("-End-"); - } - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); - - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Magic != MAGIC_PHZ) - { - throw new FileLoadException("Not a valid PHZ file!", fileFullPath); - } - - HeaderSettings.AntiAliasLevel = 1; - - FileFullPath = fileFullPath; - - - - Debug.Write("Header -> "); - Debug.WriteLine(HeaderSettings); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - uint offsetAddress = i == 0 ? HeaderSettings.PreviewSmallOffsetAddress : HeaderSettings.PreviewLargeOffsetAddress; - if (offsetAddress == 0) continue; - - inputFile.Seek(offsetAddress, SeekOrigin.Begin); - Previews[i] = Helpers.Deserialize<Preview>(inputFile); - - Debug.Write($"Preview {i} -> "); - Debug.WriteLine(Previews[i]); - - inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); - byte[] rawImageData = new byte[Previews[i].ImageLength]; - inputFile.Read(rawImageData, 0, (int)Previews[i].ImageLength); - - Thumbnails[i] = Previews[i].Decode(rawImageData); - } - - if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) - { - inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); - byte[] buffer = new byte[HeaderSettings.MachineNameSize]; - inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize); - HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer); - } - - - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; - - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - - - - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - inputFile.Seek(layerOffset, SeekOrigin.Begin); - LayerData layerData = Helpers.Deserialize<LayerData>(inputFile); - layerData.Parent = this; - LayersDefinitions[layerIndex] = layerData; - - layerOffset += (uint)Helpers.Serializer.SizeOf(layerData); - Debug.Write($"LAYER {layerIndex} -> "); - Debug.WriteLine(layerData); - - layerData.EncodedRle = new byte[layerData.DataSize]; - inputFile.Seek(layerData.DataAddress, SeekOrigin.Begin); - inputFile.Read(layerData.EncodedRle, 0, (int)layerData.DataSize); - } - - LayerManager = new LayerManager(HeaderSettings.LayerCount); - - Parallel.For(0, LayerCount, layerIndex => - { - var image = LayersDefinitions[layerIndex].Decode((uint) layerIndex, true); - this[layerIndex] = new Layer((uint) layerIndex, image); - }); - } - - public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - var baseValue = base.GetValueFromPrintParameterModifier(modifier); - if (!ReferenceEquals(baseValue, null)) return baseValue; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) return HeaderSettings.BottomLightOffDelay; - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return HeaderSettings.LayerOffTime; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) return HeaderSettings.BottomLiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) return HeaderSettings.BottomLiftSpeed; - /*if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) return PrintParametersSettings.LiftHeight; - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return PrintParametersSettings.LiftingSpeed; - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return PrintParametersSettings.RetractSpeed;*/ - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return HeaderSettings.BottomLightPWM; - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return HeaderSettings.LightPWM; - - - - return null; - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - void UpdateLayers() - { - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - // Bottom : others - LayersDefinitions[layerIndex].LayerExposure = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomExposureSeconds : HeaderSettings.LayerExposureSeconds; - LayersDefinitions[layerIndex].LayerOffTimeSeconds = layerIndex < HeaderSettings.BottomLayersCount ? HeaderSettings.BottomLightOffDelay : HeaderSettings.LayerOffTime; - } - } - - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - HeaderSettings.BottomLayersCount = - HeaderSettings.BottomLayerCount = value.Convert<uint>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - HeaderSettings.BottomExposureSeconds = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - HeaderSettings.LayerExposureSeconds = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) - { - HeaderSettings.BottomLightOffDelay = value.Convert<float>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) - { - HeaderSettings.LayerOffTime = - HeaderSettings.LayerOffTime = value.Convert<float>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) - { - HeaderSettings.BottomLiftHeight = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) - { - HeaderSettings.BottomLiftSpeed = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - HeaderSettings.LiftHeight = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - HeaderSettings.LiftSpeed = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - HeaderSettings.RetractSpeed = value.Convert<float>(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) - { - HeaderSettings.BottomLightPWM = value.Convert<ushort>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) - { - HeaderSettings.LightPWM = value.Convert<ushort>(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (LayerManager.IsModified) - { - if (!string.IsNullOrEmpty(filePath)) - { - FileFullPath = filePath; - } - Encode(FileFullPath); - return; - } - - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) - { - outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); - byte[] buffer = new byte[HeaderSettings.MachineNameSize]; - outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize); - }*/ - - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - outputFile.Seek(layerOffset, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]); - layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]); - } - outputFile.Close(); - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - if (to == typeof(ChituboxFile)) - { - ChituboxFile file = new ChituboxFile - { - LayerManager = LayerManager, - HeaderSettings - = - { - Version = 2, - BedSizeX = HeaderSettings.BedSizeX, - BedSizeY = HeaderSettings.BedSizeY, - BedSizeZ = HeaderSettings.BedSizeZ, - OverallHeightMilimeter = TotalHeight, - BottomExposureSeconds = InitialExposureTime, - BottomLayersCount = InitialLayerCount, - BottomLightPWM = HeaderSettings.BottomLightPWM, - LayerCount = LayerCount, - LayerExposureSeconds = LayerExposureTime, - LayerHeightMilimeter = LayerHeight, - LayerOffTime = HeaderSettings.LayerOffTime, - LightPWM = HeaderSettings.LightPWM, - PrintTime = HeaderSettings.PrintTime, - ProjectorType = HeaderSettings.ProjectorType, - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - AntiAliasLevel = ValidateAntiAliasingLevel() - }, - PrintParametersSettings = - { - BottomLayerCount = InitialLayerCount, - BottomLiftHeight = HeaderSettings.BottomLiftHeight, - BottomLiftSpeed = HeaderSettings.BottomLiftSpeed, - BottomLightOffDelay = HeaderSettings.BottomLightOffDelay, - CostDollars = MaterialCost, - LiftHeight = HeaderSettings.LiftHeight, - LiftSpeed = HeaderSettings.LiftSpeed, - LightOffDelay = HeaderSettings.LayerOffTime, - RetractSpeed = HeaderSettings.RetractSpeed, - VolumeMl = UsedMaterial, - WeightG = HeaderSettings.WeightG - }, - SlicerInfoSettings = {MachineName = MachineName, MachineNameSize = (uint) MachineName.Length} - }; - - - - - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ChituboxZipFile)) - { - ChituboxZipFile file = new ChituboxZipFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Filename = Path.GetFileName(FileFullPath), - - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - MachineX = HeaderSettings.BedSizeX, - MachineY = HeaderSettings.BedSizeY, - MachineZ = HeaderSettings.BedSizeZ, - MachineType = MachineName, - ProjectType = HeaderSettings.ProjectorType == 0 ? "Normal" : "LCD_mirror", - - Resin = MaterialName, - Price = MaterialCost, - Weight = HeaderSettings.WeightG, - Volume = UsedMaterial, - Mirror = (byte) (HeaderSettings.ProjectorType == 0 ? 0 : 1), - - - BottomLayerLiftHeight = HeaderSettings.BottomLiftHeight, - LayerLiftHeight = HeaderSettings.LiftHeight, - BottomLayerLiftSpeed = HeaderSettings.BottomLiftSpeed, - LayerLiftSpeed = HeaderSettings.LiftSpeed, - RetractSpeed = HeaderSettings.RetractSpeed, - BottomLayCount = InitialLayerCount, - BottomLayerCount = InitialLayerCount, - BottomLightOffTime = HeaderSettings.BottomLightOffDelay, - LayerLightOffTime = HeaderSettings.LayerOffTime, - BottomLayExposureTime = InitialExposureTime, - BottomLayerExposureTime = InitialExposureTime, - LayerExposureTime = LayerExposureTime, - LayerHeight = LayerHeight, - LayerCount = LayerCount, - AntiAliasing = ValidateAntiAliasingLevel(), - BottomLightPWM = (byte) HeaderSettings.BottomLightPWM, - LayerLightPWM = (byte) HeaderSettings.LightPWM, - - EstimatedPrintTime = PrintTime - }, - }; - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(PWSFile)) - { - PWSFile file = new PWSFile - { - LayerManager = LayerManager, - HeaderSettings = - { - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - LayerHeight = LayerHeight, - LayerExposureTime = LayerExposureTime, - LiftHeight = LiftHeight, - LiftSpeed = LiftSpeed / 60, - RetractSpeed = RetractSpeed / 60, - LayerOffTime = HeaderSettings.LayerOffTime, - BottomLayersCount = InitialLayerCount, - BottomExposureSeconds = InitialExposureTime, - Price = MaterialCost, - Volume = UsedMaterial, - Weight = HeaderSettings.WeightG, - AntiAliasing = ValidateAntiAliasingLevel() - } - }; - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ZCodexFile)) - { - TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime); - ZCodexFile file = new ZCodexFile - { - ResinMetadataSettings = new ZCodexFile.ResinMetadata - { - MaterialId = 2, - Material = MaterialName, - AdditionalSupportLayerTime = 0, - BottomLayersNumber = InitialLayerCount, - BottomLayersTime = (uint)(InitialExposureTime * 1000), - LayerTime = (uint)(LayerExposureTime * 1000), - DisableSettingsChanges = false, - LayerThickness = LayerHeight, - PrintTime = (uint)PrintTime, - TotalLayersCount = LayerCount, - TotalMaterialVolumeUsed = UsedMaterial, - TotalMaterialWeightUsed = UsedMaterial, - }, - UserSettings = new ZCodexFile.UserSettingsdata - { - Printer = MachineName, - BottomLayersCount = InitialLayerCount, - PrintTime = $"{ts.Hours}h {ts.Minutes}m", - LayerExposureTime = (uint)(LayerExposureTime * 1000), - BottomLayerExposureTime = (uint)(InitialExposureTime * 1000), - MaterialId = 2, - LayerThickness = $"{LayerHeight} mm", - AntiAliasing = (byte) (AntiAliasing > 1 ? 1 : 0), - CrossSupportEnabled = 1, - ExposureOffTime = (uint)HeaderSettings.LayerOffTime, - HollowEnabled = 0, - HollowThickness = 0, - InfillDensity = 0, - IsAdvanced = 0, - MaterialType = MaterialName, - MaterialVolume = UsedMaterial, - MaxLayer = LayerCount - 1, - ModelLiftEnabled = 0, - ModelLiftHeight = 0, - RaftEnabled = 0, - RaftHeight = 0, - RaftOffset = 0, - SupportAdditionalExposureEnabled = 0, - SupportAdditionalExposureTime = 0, - XCorrection = 0, - YCorrection = 0, - ZLiftDistance = HeaderSettings.LiftHeight, - ZLiftFeedRate = HeaderSettings.LiftSpeed, - ZLiftRetractRate = HeaderSettings.RetractSpeed, - }, - ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata - { - PrintTime = (uint)PrintTime, - PrinterName = MachineName, - Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData> - { - new ZCodexFile.ZCodeMetadata.MaterialsData - { - Name = MaterialName, - ExtruderType = "MAIN", - Id = 0, - Usage = 0, - Temperature = 0 - } - }, - }, - LayerManager = LayerManager - }; - - float usedMaterial = UsedMaterial / LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData - { - Layer = layerIndex, - UsedMaterialVolume = usedMaterial - }); - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - return true; - } - - return false; - } - #endregion - } -} diff --git a/UVtools.Parser/PWSFile.cs b/UVtools.Parser/PWSFile.cs deleted file mode 100644 index 7cbe7da..0000000 --- a/UVtools.Parser/PWSFile.cs +++ /dev/null @@ -1,1239 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; -using BinarySerialization; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace UVtools.Parser -{ - public class PWSFile : FileFormat - { - #region Constants - public const byte MarkSize = 12; - public const byte RLE1EncodingLimit = 0x7d; // 125; - public const ushort RLE4EncodingLimit = 0xfff; // 4095; - - // CRC-16-ANSI (aka CRC-16-IMB) Polynomial: x^16 + x^15 + x^2 + 1 - public static readonly int[] CRC16Table = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, - }; - - #endregion - - #region Enums - public enum LayerRleFormat - { - PWS, - PW0 - } - #endregion - - #region Sub Classes - - #region FileMark - public class FileMark - { - public const string SectionMarkFile = "ANYCUBIC"; - - private string _mark = SectionMarkFile; - /// <summary> - /// Gets the file mark placeholder - /// Fixed to "ANYCUBIC" - /// </summary> - [FieldOrder(0)] - [FieldLength(MarkSize)] - public string Mark - { - get => _mark; - set => _mark = value.TrimEnd('\0'); - } - - /// <summary> - /// Gets the file format version - /// </summary> - [FieldOrder(1)] public uint Version { get; set; } = 1; - - /// <summary> - /// Gets the area num - /// </summary> - [FieldOrder(2)] public uint AreaNum { get; set; } = 4; - - /// <summary> - /// Gets the header start address - /// </summary> - [FieldOrder(3)] public uint HeaderAddress { get; set; } - - [FieldOrder(4)] public uint Offset1 { get; set; } - - /// <summary> - /// Gets the preview start offset - /// </summary> - [FieldOrder(5)] public uint PreviewAddress { get; set; } - - [FieldOrder(6)] public uint Offset2 { get; set; } - - /// <summary> - /// Gets the layer definition start address - /// </summary> - [FieldOrder(7)] public uint LayerDefinitionAddress { get; set; } - - [FieldOrder(8)] public uint Offset3 { get; set; } - - /// <summary> - /// Gets layer image start address - /// </summary> - [FieldOrder(9)] public uint LayerImageAddress { get; set; } - - public override string ToString() - { - return $"{nameof(Mark)}: {Mark}, {nameof(Version)}: {Version}, {nameof(AreaNum)}: {AreaNum}, {nameof(HeaderAddress)}: {HeaderAddress}, {nameof(Offset1)}: {Offset1}, {nameof(PreviewAddress)}: {PreviewAddress}, {nameof(Offset2)}: {Offset2}, {nameof(LayerDefinitionAddress)}: {LayerDefinitionAddress}, {nameof(Offset3)}: {Offset3}, {nameof(LayerImageAddress)}: {LayerImageAddress}"; - } - } - #endregion - - #region Section - - public class Section - { - private string _mark; - - /// <summary> - /// Gets the section mark placeholder - /// </summary> - [FieldOrder(0)] - [FieldLength(MarkSize)] - public string Mark - { - get => _mark; - set => _mark = value.TrimEnd('\0'); - } - - /// <summary> - /// Gets the length of this section - /// </summary> - [FieldOrder(1)] public uint Length { get; set; } - - public Section() { } - - public Section(string mark, object obj) : this(mark, (uint)Helpers.Serializer.SizeOf(obj)) { } - - public Section(string mark, uint length = 0) - { - Mark = mark; - Length = length; - } - - - public void Validate(string mark, object obj = null) - { - Validate(mark, 0u, obj); - } - - public void Validate(string mark, uint length, object obj = null) - { - if (!Mark.Equals(mark)) - { - throw new FileLoadException($"'{Mark}' section expected, but got '{mark}'"); - } - - if (!ReferenceEquals(obj, null)) - { - length += (uint)Helpers.Serializer.SizeOf(obj); - } - - if (length > 0 && Length != length) - { - throw new FileLoadException($"{Mark} section bytes: expected {Length}, got {length}, difference: {(int)Length - length}"); - } - } - - public override string ToString() => $"{{{nameof(Mark)}: {Mark}, {nameof(Length)}: {Length}}}"; - } - - #endregion - - #region Header - public class Header - { - public const string SectionMark = "HEADER"; - - [Ignore] public Section Section { get; set; } - [FieldOrder(0)] public float PixelSize { get; set; } - [FieldOrder(1)] public float LayerHeight { get; set; } - [FieldOrder(2)] public float LayerExposureTime { get; set; } - [FieldOrder(3)] public float LayerOffTime { get; set; } = 1; - [FieldOrder(4)] public float BottomExposureSeconds { get; set; } - [FieldOrder(5)] public float BottomLayersCount { get; set; } - [FieldOrder(6)] public float LiftHeight { get; set; } = 6; - - /// <summary> - /// Gets the lift speed in mm/s - /// </summary> - [FieldOrder(7)] public float LiftSpeed { get; set; } = 3; // mm/s - - /// <summary> - /// Gets the retract speed in mm/s - /// </summary> - [FieldOrder(8)] public float RetractSpeed { get; set; } = 3; // mm/s - [FieldOrder(9)] public float Volume { get; set; } - [FieldOrder(10)] public uint AntiAliasing { get; set; } = 1; - [FieldOrder(11)] public uint ResolutionX { get; set; } - [FieldOrder(12)] public uint ResolutionY { get; set; } - [FieldOrder(13)] public float Weight { get; set; } - [FieldOrder(14)] public float Price { get; set; } - [FieldOrder(15)] public uint ResinType { get; set; } // 0x24 ? - [FieldOrder(16)] public uint PerLayerOverride { get; set; } // bool - [FieldOrder(17)] public uint Offset1 { get; set; } - [FieldOrder(18)] public uint Offset2 { get; set; } - [FieldOrder(19)] public uint Offset3 { get; set; } - - public Header() - { - Section = new Section(SectionMark, this); - } - - public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(PixelSize)}: {PixelSize}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(LayerExposureTime)}: {LayerExposureTime}, {nameof(LayerOffTime)}: {LayerOffTime}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(Volume)}: {Volume}, {nameof(AntiAliasing)}: {AntiAliasing}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Weight)}: {Weight}, {nameof(Price)}: {Price}, {nameof(ResinType)}: {ResinType}, {nameof(PerLayerOverride)}: {PerLayerOverride}, {nameof(Offset1)}: {Offset1}, {nameof(Offset2)}: {Offset2}, {nameof(Offset3)}: {Offset3}"; - - public void Validate() - { - Section.Validate(SectionMark, this); - } - } - - #endregion - - #region Preview - - /// <summary> - /// The files contain two preview images. - /// These are shown on the printer display when choosing which file to print, sparing the poor printer from needing to render a 3D image from scratch. - /// </summary> - public class Preview - { - public const string SectionMark = "PREVIEW"; - [Ignore] public Section Section { get; set; } - - /// <summary> - /// Gets the image width, in pixels. - /// </summary> - [FieldOrder(1)] public uint Width { get; set; } = 224; - - /// <summary> - /// Gets the resolution of the image, in dpi. - /// </summary> - [FieldOrder(2)] public uint Resolution { get; set; } = 42; - - /// <summary> - /// Gets the image height, in pixels. - /// </summary> - [FieldOrder(3)] public uint Height { get; set; } = 168; - - // little-endian 16bit colors, RGB 565 encoded. - //[FieldOrder(4)] - //[FieldLength("Section.Length")] - [Ignore] - public byte[] Data { get; set; } - - public Preview() - { - Section = new Section(SectionMark, this); - } - - public Image<Rgba32> Decode(bool consumeData = true) - { - Image<Rgba32> image = new Image<Rgba32>((int) Width, (int) Height); - if (!image.TryGetSinglePixelSpan(out var span)) return null; - - int pixel = 0; - for (uint i = 0; i < Data.Length; i += 2) - { - ushort color16 = (ushort)(Data[i] + (Data[i+1] << 8)); - var r =(color16 >> 11) & 0x1f; - var g = (color16 >> 5) & 0x3f; - var b = (color16 >> 0) & 0x1f; - - span[pixel++] = new Rgba32( - (byte)((r << 3) | (r & 0x7)), - (byte)((g << 2) | (g & 0x3)), - (byte)((b << 3) | (b & 0x7)) - ); - } - - if (consumeData) - Data = null; - - return image; - } - - public static Preview Encode(Image<Rgba32> image) - { - if (!image.TryGetSinglePixelSpan(out var span)) return null; - - Preview preview = new Preview - { - Width = (uint) image.Width, - Height = (uint) image.Height, - Resolution = (uint) image.Metadata.HorizontalResolution, - Data = new byte[span.Length * 2] - }; - - for (int i = 0; i < span.Length; i++) - { - int r = span[i].R >> 3; - int g = span[i].G >> 2; - int b = span[i].B >> 3; - - ushort color = (ushort) ((r << 11) | (g << 5) | (b << 0)); - - preview.Data[i * 2] = (byte) color; - preview.Data[i * 2 + 1] = (byte) (color >> 8); - } - - preview.Section.Length += (uint) preview.Data.Length; - return preview; - } - - public override string ToString() - { - return $"{nameof(Section)}: {Section}, {nameof(Width)}: {Width}, {nameof(Resolution)}: {Resolution}, {nameof(Height)}: {Height}, {nameof(Data)}: {Data}"; - } - - public void Validate(uint size) - { - Section.Validate(SectionMark, size, this); - } - } - - #endregion - - #region Layer - - public class LayerData - { - /// <summary> - /// Gets the layer image offset to encoded layer data, and its length in bytes. - /// </summary> - [FieldOrder(0)] - public uint DataAddress { get; set; } - - /// <summary> - /// Gets the layer image length in bytes. - /// </summary> - [FieldOrder(1)] - public uint DataLength { get; set; } - - [FieldOrder(2)] public float LiftHeight { get; set; } - - [FieldOrder(3)] public float LiftSpeed { get; set; } - - /// <summary> - /// Gets the exposure time for this layer, in seconds. - /// </summary> - [FieldOrder(4)] - public float LayerExposure { get; set; } - - /// <summary> - /// Gets the build platform Z position for this layer, measured in millimeters. - /// </summary> - [FieldOrder(5)] - public float LayerPositionZ { get; set; } - - [FieldOrder(6)] public float Offset1 { get; set; } - [FieldOrder(7)] public float Offset2 { get; set; } - - [Ignore] public byte[] EncodedRle { get; set; } - [Ignore] public PWSFile Parent { get; set; } - - public LayerData() - { - } - - public LayerData(PWSFile parent, uint layerIndex) - { - Parent = parent; - LiftHeight = Parent.HeaderSettings.LiftHeight; - LiftSpeed = Parent.HeaderSettings.LiftSpeed; - LayerExposure = layerIndex < Parent.InitialLayerCount ? Parent.HeaderSettings.BottomExposureSeconds : Parent.HeaderSettings.LayerExposureTime; - LayerPositionZ = Parent.GetHeightFromLayer(layerIndex); - } - - public Image<L8> Decode(bool consumeData = true) - { - var result = Parent.LayerFormat == LayerRleFormat.PWS ? DecodePWS() : DecodePW0(); - if (consumeData) - EncodedRle = null; - - return result; - } - - public byte[] Encode(Image<L8> image) - { - EncodedRle = Parent.LayerFormat == LayerRleFormat.PWS ? EncodePWS(image) : EncodePW0(image); - return EncodedRle; - } - - private Image<L8> DecodePWS() - { - var image = new Image<L8>((int) Parent.ResolutionX, (int) Parent.ResolutionY); - image.TryGetSinglePixelSpan(out var span); - - int index = 0; - for (byte bit = 0; bit < Parent.AntiAliasing; bit++) - { - byte bitValue = (byte)(byte.MaxValue / ((1 << Parent.AntiAliasing) - 1) * (1 << bit)); - - int n = 0; - for (; index < EncodedRle.Length; index++) - { - // Lower 7 bits is the repeat count for the bit (0..127) - int reps = EncodedRle[index] & 0x7f; - - // We only need to set the non-zero pixels - // High bit is on for white, off for black - if ((EncodedRle[index] & 0x80) != 0) - { - for (int i = 0; i < reps; i++) - { - span[n + i].PackedValue |= bitValue; - } - } - - n += reps; - - if (n == span.Length) - { - index++; - break; - } - - if (n > span.Length) - { - throw new FileLoadException("Error image ran off the end"); - } - } - } - - return image; - } - - public byte[] EncodePWS(Image<L8> image) - { - List<byte> rawData = new List<byte>(); - - bool obit; - int rep; - - void AddRep() - { - if (rep <= 0) return; - - byte by = (byte)rep; - - if (obit) - { - by |= 0x80; - //bitsOn += uint(rep) - } - - rawData.Add(by); - } - - for (byte aalevel = 0; aalevel < Parent.AntiAliasing; aalevel++) - { - obit = false; - rep = 0; - - for (int y = 0; y < image.Height; y++) - { - Span<L8> pixelRowSpan = image.GetPixelRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - var nbit = (pixelRowSpan[x].PackedValue & (1 << (8 - Parent.AntiAliasing + aalevel))) != 0; - - if (nbit == obit) - { - rep++; - - if (rep == RLE1EncodingLimit) - { - AddRep(); - rep = 0; - } - } - else - { - AddRep(); - obit = nbit; - rep = 1; - } - } - } - - // Collect stragglers - AddRep(); - } - - DataLength = (uint) rawData.Count; - - return rawData.ToArray(); - } - - private Image<L8> DecodePW0() - { - var image = new Image<L8>((int) Parent.ResolutionX, (int) Parent.ResolutionY); - image.TryGetSinglePixelSpan(out var span); - - uint n = 0; - for (int index = 0; index < EncodedRle.Length; index++) - { - byte b = EncodedRle[index]; - int code = (b >> 4); - uint reps = (uint) (b & 0xf); - byte color; - switch (code) - { - case 0x0: - color = 0x00; - index++; - reps = reps * 256 + EncodedRle[index]; - break; - case 0xf: - color = 0xff; - index++; - reps = reps * 256 + EncodedRle[index]; - break; - default: - color = (byte) ((code << 4) | code); - break; - } - - color &= 0xff; - - // We only need to set the non-zero pixels - if (color != 0) - { - for (int i = 0; i < reps; i++) - { - span[(int) (n + i)].PackedValue |= color; - } - } - - n += reps; - - - if (n == span.Length) - { - //index++; - break; - } - - if (n > span.Length) - { - throw new FileLoadException($"Error image ran off the end: {n - reps}({reps}) of {span.Length}"); - } - } - - if (n != span.Length) - { - throw new FileLoadException($"Error image ended short: {n} of {span.Length}"); - } - - return image; - } - - public byte[] EncodePW0(Image<L8> image) - { - List<byte> rawData = new List<byte>(); - - int lastColor = -1; - int reps = 0; - - void PutReps() - { - while (reps > 0) - { - int done = reps; - - if (lastColor == 0 || lastColor == 0xf) - { - if (done > RLE4EncodingLimit) - { - done = RLE4EncodingLimit; - } - //more:= []byte{ 0, 0} - //binary.BigEndian.PutUint16(more, uint16(done | (color << 12))) - - //rle = append(rle, more...) - - ushort more = (ushort)(done | (lastColor << 12)); - rawData.Add((byte)(more >> 8)); - rawData.Add((byte)more); - } - else - { - if (done > 0xf) - { - done = 0xf; - } - rawData.Add((byte)(done | lastColor << 4)); - } - - reps -= done; - } - } - - image.TryGetSinglePixelSpan(out var span); - - for (int i = 0; i < span.Length; i++) - { - int color = span[i].PackedValue >> 4; - - if (color == lastColor) - { - reps++; - } - else - { - PutReps(); - lastColor = color; - reps = 1; - } - } - - PutReps(); - - EncodedRle = rawData.ToArray(); - DataLength = (uint)rawData.Count; - - ushort crc = CRCRle4(EncodedRle); - rawData.Add((byte)(crc >> 8)); - rawData.Add((byte)crc); - - return EncodedRle; - } - - public static ushort CRCRle4(byte[] data) - { - ushort crc16 = 0; - for (int i = 0; i < data.Length; i++) - { - crc16 = (ushort) ((crc16 << 8) ^ CRC16Table[((crc16 >> 8) ^ CRC16Table[data[i]]) & 0xff]); - - } - - crc16 = (ushort) ((CRC16Table[crc16 & 0xff] * 0x100) + CRC16Table[(crc16 >> 8) & 0xff]); - - return crc16; - } - - public ushort CRCEncodedRle() - { - return CRCRle4(EncodedRle); - } - } - - #endregion - - #region LayerDefinition - public class LayerDefinition - { - public const string SectionMark = "LAYERDEF"; - - [Ignore] public Section Section { get; set; } = new Section(SectionMark); - - [FieldOrder(0)] public uint LayersCount { get; set; } - - [Ignore] public LayerData[] Layers; - - public LayerDefinition() - { - Section = new Section(SectionMark, this); - } - - public LayerDefinition(uint layersCount) : this() - { - LayersCount = layersCount; - Layers = new LayerData[layersCount]; - } - - [Ignore] - public LayerData this[uint index] - { - get => Layers[index]; - set => Layers[index] = value; - } - - [Ignore] - public LayerData this[int index] - { - get => Layers[index]; - set => Layers[index] = value; - } - - public void Validate() - { - Section.Validate(SectionMark, (uint)(LayersCount * Helpers.Serializer.SizeOf(new LayerData())), this); - } - - public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(LayersCount)}: {LayersCount}"; - } - #endregion - - #endregion - - #region Properties - - public FileMark FileMarkSettings { get; protected internal set; } = new FileMark(); - - public Header HeaderSettings { get; protected internal set; } = new Header(); - - public Preview PreviewSettings { get; protected internal set; } = new Preview(); - - public LayerDefinition LayersDefinition { get; private set; } = new LayerDefinition(); - - public Dictionary<string, LayerData> LayersHash { get; } = new Dictionary<string, LayerData>(); - - public override FileFormatType FileType => FileFormatType.Binary; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("pws", "Photon Workshop PWS Files"), - new FileExtension("pw0", "Photon Workshop PW0 Files") - }; - - public override Type[] ConvertToFormats { get; } = - { - //typeof(ChituboxZipFile) - //typeof(PHZFile), - //typeof(ZCodexFile), - }; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = - { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - //PrintParameterModifier.BottomLayerOffTime, - PrintParameterModifier.LayerOffTime, - //PrintParameterModifier.BottomLiftHeight, - //PrintParameterModifier.BottomLiftSpeed, - PrintParameterModifier.LiftHeight, - PrintParameterModifier.LiftSpeed, - PrintParameterModifier.RetractSpeed, - }; - - public override byte ThumbnailsCount { get; } = 1; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(224, 168)}; - - public override uint ResolutionX => HeaderSettings.ResolutionX; - - public override uint ResolutionY => HeaderSettings.ResolutionY; - public override byte AntiAliasing => (byte) HeaderSettings.AntiAliasing; - - public override float LayerHeight => HeaderSettings.LayerHeight; - - public override ushort InitialLayerCount => (ushort)HeaderSettings.BottomLayersCount; - - public override float InitialExposureTime => HeaderSettings.BottomExposureSeconds; - - public override float LayerExposureTime => HeaderSettings.LayerExposureTime; - public override float LiftHeight => HeaderSettings.LiftHeight; - public override float LiftSpeed => HeaderSettings.LiftSpeed * 60; - public override float RetractSpeed => HeaderSettings.RetractSpeed * 60; - - public override float PrintTime => 0; - - public override float UsedMaterial => HeaderSettings.Volume; - - public override float MaterialCost => HeaderSettings.Price; - - public override string MaterialName => null; - public override string MachineName => LayerFormat == LayerRleFormat.PWS ? "AnyCubic Photon S" : "AnyCubic Photon Zero"; - - public override object[] Configs => new object[] { FileMarkSettings, HeaderSettings, PreviewSettings, LayersDefinition }; - - public LayerRleFormat LayerFormat => FileFullPath.EndsWith(".pws") ? LayerRleFormat.PWS : LayerRleFormat.PW0; - - #endregion - - #region Constructors - public PWSFile() - { - } - #endregion - - #region Methods - public override void Clear() - { - base.Clear(); - - LayersDefinition = null; - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - LayersHash.Clear(); - - LayersDefinition = new LayerDefinition(LayerCount); - - uint currentOffset = FileMarkSettings.HeaderAddress = (uint) Helpers.Serializer.SizeOf(FileMarkSettings); - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, HeaderSettings.Section); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - if (CreatedThumbnailsCount > 0) - { - FileMarkSettings.PreviewAddress = currentOffset; - Preview preview = Preview.Encode(Thumbnails[0]); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, preview.Section); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, preview); - currentOffset += outputFile.WriteBytes(preview.Data); - } - - FileMarkSettings.LayerDefinitionAddress = currentOffset; - - Parallel.For(0, LayerCount, layerIndex => - { - LayerData layer = new LayerData(this, (uint) layerIndex); - layer.Encode(this[layerIndex].Image); - LayersDefinition.Layers[layerIndex] = layer; - }); - - LayersDefinition.Section.Length += (uint)Helpers.Serializer.SizeOf(LayersDefinition[0]) * LayerCount; - currentOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinition.Section); - uint offsetLayerRle = FileMarkSettings.LayerImageAddress = currentOffset + LayersDefinition.Section.Length; - - currentOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinition); - - - foreach (var layer in LayersDefinition.Layers) - { - string hash = Helpers.ComputeSHA1Hash(layer.EncodedRle); - - if (LayersHash.TryGetValue(hash, out var layerDataHash)) - { - layer.DataAddress = layerDataHash.DataAddress; - layer.DataLength = (uint)layerDataHash.EncodedRle.Length; - } - else - { - LayersHash.Add(hash, layer); - - layer.DataAddress = offsetLayerRle; - - outputFile.Seek(offsetLayerRle, SeekOrigin.Begin); - offsetLayerRle += Helpers.SerializeWriteFileStream(outputFile, layer.EncodedRle); - } - - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layer); - } - - // Rewind - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, FileMarkSettings); - - } - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); - - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - FileMarkSettings = Helpers.Deserialize<FileMark>(inputFile); - - Debug.Write("FileMark -> "); - Debug.WriteLine(FileMarkSettings); - - if (!FileMarkSettings.Mark.Equals(FileMark.SectionMarkFile)) - { - throw new FileLoadException($"Invalid Filemark {FileMarkSettings.Mark}, expected {FileMark.SectionMarkFile}", fileFullPath); - } - - if (FileMarkSettings.Version != 1) - { - throw new FileLoadException($"Invalid Version {FileMarkSettings.Version}, expected 1", fileFullPath); - } - - FileFullPath = fileFullPath; - - inputFile.Seek(FileMarkSettings.HeaderAddress, SeekOrigin.Begin); - //Section sectionHeader = Helpers.Deserialize<Section>(inputFile); - //Debug.Write("SectionHeader -> "); - //Debug.WriteLine(sectionHeader); - - var section = Helpers.Deserialize<Section>(inputFile); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - HeaderSettings.Section = section; - - - Debug.Write("Header -> "); - Debug.WriteLine(HeaderSettings); - - HeaderSettings.Validate(); - - if (FileMarkSettings.PreviewAddress > 0) - { - inputFile.Seek(FileMarkSettings.PreviewAddress, SeekOrigin.Begin); - - section = Helpers.Deserialize<Section>(inputFile); - PreviewSettings = Helpers.Deserialize<Preview>(inputFile); - PreviewSettings.Section = section; - Debug.Write("Preview -> "); - Debug.WriteLine(PreviewSettings); - - uint datasize = PreviewSettings.Width * PreviewSettings.Height * 2; - PreviewSettings.Validate(datasize); - - PreviewSettings.Data = new byte[datasize]; - inputFile.ReadBytes(PreviewSettings.Data); - - Thumbnails[0] = PreviewSettings.Decode(true); - } - - inputFile.Seek(FileMarkSettings.LayerDefinitionAddress, SeekOrigin.Begin); - - section = Helpers.Deserialize<Section>(inputFile); - LayersDefinition = Helpers.Deserialize<LayerDefinition>(inputFile); - LayersDefinition.Section = section; - Debug.Write("LayersDefinition -> "); - Debug.WriteLine(LayersDefinition); - - LayerManager = new LayerManager(LayersDefinition.LayersCount); - LayersDefinition.Layers = new LayerData[LayerCount]; - - - LayersDefinition.Validate(); - - for (int i = 0; i < LayerCount; i++) - { - LayersDefinition[i] = Helpers.Deserialize<LayerData>(inputFile); - LayersDefinition[i].Parent = this; - } - - for (int i = 0; i < LayerCount; i++) - { - var layer = LayersDefinition[i]; - inputFile.Seek(layer.DataAddress, SeekOrigin.Begin); - layer.EncodedRle = new byte[layer.DataLength]; - inputFile.ReadBytes(layer.EncodedRle); - - /*if (LayerFormat == LayerRleFormat.PW0) - { - var crcBytes = new byte[2]; - inputFile.Read(crcBytes, 0, 2); - ushort crcExpected = BitConverter.ToUInt16(crcBytes, 0); - ushort crcEncodedRle = LayersDefinition.Layers[i].CRCEncodedRle(); - - if (crcExpected != crcEncodedRle) - { - Debug.WriteLine($"Error: Checksum expected {crcExpected}, got {crcEncodedRle}"); - } - }*/ - } - - Parallel.For(0, LayerCount, layerIndex => { - this[layerIndex] = new Layer((uint)layerIndex, LayersDefinition[(uint) layerIndex].Decode()); - }); - } - - public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) - { - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return HeaderSettings.LayerOffTime; - - var baseValue = base.GetValueFromPrintParameterModifier(modifier); - return baseValue; - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - void UpdateLayers() - { - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - // Bottom : others - LayersDefinition[layerIndex].LayerExposure = layerIndex < HeaderSettings.BottomLayersCount - ? HeaderSettings.BottomExposureSeconds - : HeaderSettings.LayerExposureTime; - } - } - - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - HeaderSettings.BottomLayersCount = - HeaderSettings.BottomLayersCount = value.Convert<uint>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - HeaderSettings.BottomExposureSeconds = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - HeaderSettings.LayerExposureTime = value.Convert<float>(); - UpdateLayers(); - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) - { - HeaderSettings.LayerOffTime = value.Convert<float>(); - UpdateLayers(); - return true; - } - - - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - HeaderSettings.LiftHeight = value.Convert<float>(); - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - HeaderSettings.LiftSpeed = value.Convert<float>() / 60f; - UpdateLayers(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - HeaderSettings.RetractSpeed = value.Convert<float>() / 60f; - UpdateLayers(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (LayerManager.IsModified) - { - if (!string.IsNullOrEmpty(filePath)) - { - FileFullPath = filePath; - } - Encode(FileFullPath); - return; - } - - - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(FileMarkSettings.HeaderAddress+Helpers.Serializer.SizeOf(HeaderSettings.Section), SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - - outputFile.Seek(FileMarkSettings.LayerDefinitionAddress + Helpers.Serializer.SizeOf(HeaderSettings.Section) + Helpers.Serializer.SizeOf(LayersDefinition), SeekOrigin.Begin); - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]); - } - outputFile.Close(); - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - /*if (to == typeof(PHZFile)) - { - PHZFile file = new PHZFile - { - LayerManager = LayerManager - }; - - - file.HeaderSettings.Version = 2; - file.HeaderSettings.BedSizeX = HeaderSettings.BedSizeX; - file.HeaderSettings.BedSizeY = HeaderSettings.BedSizeY; - file.HeaderSettings.BedSizeZ = HeaderSettings.BedSizeZ; - file.HeaderSettings.OverallHeightMilimeter = TotalHeight; - file.HeaderSettings.BottomExposureSeconds = InitialExposureTime; - file.HeaderSettings.BottomLayersCount = InitialLayerCount; - file.HeaderSettings.BottomLightPWM = HeaderSettings.BottomLightPWM; - file.HeaderSettings.LayerCount = LayerCount; - file.HeaderSettings.LayerExposureSeconds = LayerExposureTime; - file.HeaderSettings.LayerHeightMilimeter = LayerHeight; - file.HeaderSettings.LayerOffTime = HeaderSettings.LayerOffTime; - file.HeaderSettings.LightPWM = HeaderSettings.LightPWM; - file.HeaderSettings.PrintTime = HeaderSettings.PrintTime; - file.HeaderSettings.ProjectorType = HeaderSettings.ProjectorType; - file.HeaderSettings.ResolutionX = ResolutionX; - file.HeaderSettings.ResolutionY = ResolutionY; - - file.HeaderSettings.BottomLayerCount = InitialLayerCount; - file.HeaderSettings.BottomLiftHeight = PrintParametersSettings.BottomLiftHeight; - file.HeaderSettings.BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed; - file.HeaderSettings.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay; - file.HeaderSettings.CostDollars = MaterialCost; - file.HeaderSettings.LiftHeight = PrintParametersSettings.LiftHeight; - file.HeaderSettings.LiftingSpeed = PrintParametersSettings.LiftingSpeed; - file.HeaderSettings.LayerOffTime = HeaderSettings.LayerOffTime; - file.HeaderSettings.RetractSpeed = PrintParametersSettings.RetractSpeed; - file.HeaderSettings.VolumeMl = UsedMaterial; - file.HeaderSettings.WeightG = PrintParametersSettings.WeightG; - - file.HeaderSettings.MachineName = MachineName; - file.HeaderSettings.MachineNameSize = (uint) MachineName.Length; - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ZCodexFile)) - { - TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime); - ZCodexFile file = new ZCodexFile - { - ResinMetadataSettings = new ZCodexFile.ResinMetadata - { - MaterialId = 2, - Material = MaterialName, - AdditionalSupportLayerTime = 0, - BottomLayersNumber = InitialLayerCount, - BottomLayersTime = (uint)(InitialExposureTime * 1000), - LayerTime = (uint)(LayerExposureTime * 1000), - DisableSettingsChanges = false, - LayerThickness = LayerHeight, - PrintTime = (uint)PrintTime, - TotalLayersCount = LayerCount, - TotalMaterialVolumeUsed = UsedMaterial, - TotalMaterialWeightUsed = UsedMaterial, - }, - UserSettings = new ZCodexFile.UserSettingsdata - { - Printer = MachineName, - BottomLayersCount = InitialLayerCount, - PrintTime = $"{ts.Hours}h {ts.Minutes}m", - LayerExposureTime = (uint)(LayerExposureTime * 1000), - BottomLayerExposureTime = (uint)(InitialExposureTime * 1000), - MaterialId = 2, - LayerThickness = $"{LayerHeight} mm", - AntiAliasing = 0, - CrossSupportEnabled = 1, - ExposureOffTime = (uint) HeaderSettings.LayerOffTime, - HollowEnabled = 0, - HollowThickness = 0, - InfillDensity = 0, - IsAdvanced = 0, - MaterialType = MaterialName, - MaterialVolume = UsedMaterial, - MaxLayer = LayerCount - 1, - ModelLiftEnabled = 0, - ModelLiftHeight = 0, - RaftEnabled = 0, - RaftHeight = 0, - RaftOffset = 0, - SupportAdditionalExposureEnabled = 0, - SupportAdditionalExposureTime = 0, - XCorrection = 0, - YCorrection = 0, - ZLiftDistance = PrintParametersSettings.LiftHeight, - ZLiftFeedRate = PrintParametersSettings.LiftingSpeed, - ZLiftRetractRate = PrintParametersSettings.RetractSpeed, - }, - ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata - { - PrintTime = (uint)PrintTime, - PrinterName = MachineName, - Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData> - { - new ZCodexFile.ZCodeMetadata.MaterialsData - { - Name = MaterialName, - ExtruderType = "MAIN", - Id = 0, - Usage = 0, - Temperature = 0 - } - }, - }, - LayerManager = LayerManager - }; - - float usedMaterial = UsedMaterial / LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData - { - Layer = layerIndex, - UsedMaterialVolume = usedMaterial - }); - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - return true; - } - */ - return false; - } - #endregion - } -} diff --git a/UVtools.Parser/SL1File.cs b/UVtools.Parser/SL1File.cs deleted file mode 100644 index a647308..0000000 --- a/UVtools.Parser/SL1File.cs +++ /dev/null @@ -1,968 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using UVtools.Parser; - -namespace UVtools.Parser -{ - public class SL1File : FileFormat - { - #region Constants - - public const string Keyword_AntiAliasing = "AntiAliasing"; - public const string Keyword_BottomLightOffDelay = "BottomLightOffDelay"; - public const string Keyword_LayerOffTime = "LayerOffTime"; - public const string Keyword_LightOffDelay = "LightOffDelay"; - public const string Keyword_BottomLiftHeight = "BottomLiftHeight"; - public const string Keyword_BottomLiftSpeed = "BottomLiftSpeed"; - public const string Keyword_LiftHeight = "LiftHeight"; - public const string Keyword_LiftSpeed = "LiftSpeed"; - public const string Keyword_RetractSpeed = "RetractSpeed"; - public const string Keyword_BottomLightPWM = "BottomLightPWM"; - public const string Keyword_LightPWM = "LightPWM"; - #endregion - - #region Sub Classes - - #region Printer - public class Printer - { - #region Printer - public string InheritsCummulative { get; set; } - public string PrinterSettingsId { get; set; } - public string PrinterTechnology { get; set; } - public string PrinterModel { get; set; } - public string PrinterVariant { get; set; } - public string PrinterVendor { get; set; } - public string DefaultSlaMaterialProfile { get; set; } - public string DefaultSlaPrintProfile { get; set; } - public string PrinterNotes { get; set; } - public string Thumbnails { get; set; } - #endregion - - #region Size and Coordinates - public string BedCustomModel { get; set; } - public string BedCustomTexture { get; set; } - public string BedShape { get; set; } - public float MaxPrintHeight { get; set; } - #endregion - - #region Display - public float DisplayWidth { get; set; } - public float DisplayHeight { get; set; } - public uint DisplayPixelsX { get; set; } - public uint DisplayPixelsY { get; set; } - public string DisplayOrientation { get; set; } - public bool DisplayMirrorX { get; set; } - public bool DisplayMirrorY { get; set; } - #endregion - - #region Tilt - public byte FastTiltTime { get; set; } - public byte SlowTiltTime { get; set; } - public byte AreaFill { get; set; } - #endregion - - #region Corrections - public string RelativeCorrection { get; set; } - public byte AbsoluteCorrection { get; set; } - public float ElefantFootCompensation { get; set; } - public float ElefantFootMinWidth { get; set; } - public byte GammaCorrection { get; set; } - - #endregion - - #region Exposure - - public byte MinExposureTime { get; set; } - public byte MaxExposureTime { get; set; } - public byte MinInitialExposureTime { get; set; } - public ushort MaxInitialExposureTime { get; set; } - - #endregion - - #region Overrides - public override string ToString() - { - return $"{nameof(PrinterSettingsId)}: {PrinterSettingsId}, {nameof(PrinterTechnology)}: {PrinterTechnology}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrinterVendor)}: {PrinterVendor}, {nameof(DefaultSlaMaterialProfile)}: {DefaultSlaMaterialProfile}, {nameof(DefaultSlaPrintProfile)}: {DefaultSlaPrintProfile}, {nameof(PrinterNotes)}: {PrinterNotes}, {nameof(Thumbnails)}: {Thumbnails}, {nameof(BedCustomModel)}: {BedCustomModel}, {nameof(BedCustomTexture)}: {BedCustomTexture}, {nameof(BedShape)}: {BedShape}, {nameof(MaxPrintHeight)}: {MaxPrintHeight}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayPixelsX)}: {DisplayPixelsX}, {nameof(DisplayPixelsY)}: {DisplayPixelsY}, {nameof(DisplayOrientation)}: {DisplayOrientation}, {nameof(DisplayMirrorX)}: {DisplayMirrorX}, {nameof(DisplayMirrorY)}: {DisplayMirrorY}, {nameof(FastTiltTime)}: {FastTiltTime}, {nameof(SlowTiltTime)}: {SlowTiltTime}, {nameof(AreaFill)}: {AreaFill}, {nameof(RelativeCorrection)}: {RelativeCorrection}, {nameof(AbsoluteCorrection)}: {AbsoluteCorrection}, {nameof(ElefantFootCompensation)}: {ElefantFootCompensation}, {nameof(ElefantFootMinWidth)}: {ElefantFootMinWidth}, {nameof(GammaCorrection)}: {GammaCorrection}, {nameof(MinExposureTime)}: {MinExposureTime}, {nameof(MaxExposureTime)}: {MaxExposureTime}, {nameof(MinInitialExposureTime)}: {MinInitialExposureTime}, {nameof(MaxInitialExposureTime)}: {MaxInitialExposureTime}"; - } - #endregion - } - #endregion - - #region Material - public class Material - { - #region Material - public string MaterialVendor { get; set; } - public string MaterialType { get; set; } - public string SlaMaterialSettingsId { get; set; } - public float BottleCost { get; set; } - public ushort BottleVolume { get; set; } - public float BottleWeight { get; set; } - public float MaterialDensity { get; set; } - public string MaterialNotes { get; set; } - - #endregion - - #region Layers - - public float InitialLayerHeight { get; set; } - #endregion - - #region Exposure - - public float ExposureTime { get; set; } - public float InitialExposureTime { get; set; } - #endregion - - #region Corrections - public string MaterialCorrection { get; set; } - - #endregion - - #region Dependencies - - public string CompatiblePrintersConditionCummulative { get; set; } - public string CompatiblePrintsConditionCummulative { get; set; } - - #endregion - - #region Overrides - public override string ToString() - { - return $"{nameof(MaterialVendor)}: {MaterialVendor}, {nameof(MaterialType)}: {MaterialType}, {nameof(SlaMaterialSettingsId)}: {SlaMaterialSettingsId}, {nameof(BottleCost)}: {BottleCost}, {nameof(BottleVolume)}: {BottleVolume}, {nameof(BottleWeight)}: {BottleWeight}, {nameof(MaterialDensity)}: {MaterialDensity}, {nameof(MaterialNotes)}: {MaterialNotes}, {nameof(InitialLayerHeight)}: {InitialLayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(InitialExposureTime)}: {InitialExposureTime}, {nameof(MaterialCorrection)}: {MaterialCorrection}, {nameof(CompatiblePrintersConditionCummulative)}: {CompatiblePrintersConditionCummulative}, {nameof(CompatiblePrintsConditionCummulative)}: {CompatiblePrintsConditionCummulative}"; - } - #endregion - } - #endregion - - #region Print - - public class Print - { - #region Print - public string SlaPrintSettingsId { get; set; } - #endregion - - #region Layers - - public float LayerHeight { get; set; } - public byte FadedLayers { get; set; } - #endregion - - #region Supports - public bool SupportsEnable { get; set; } - - - public float SupportHeadFrontDiameter { get; set; } - public float SupportHeadPenetration { get; set; } - public float SupportHeadWidth { get; set; } - - public byte SupportPillarWideningFactor { set; get; } - public float SupportPillarDiameter { get; set; } - public float SupportMaxBridgesOnPillar { get; set; } - public string SupportPillarConnectionMode { get; set; } - public bool SupportBuildplateOnly { get; set; } - public float SupportBaseDiameter { get; set; } - public float SupportBaseHeight { get; set; } - public float SupportBaseSafetyDistance { get; set; } - public bool PadAroundObject { get; set; } - public float SupportObjectElevation { get; set; } - - - public ushort SupportCriticalAngle { get; set; } - public float SupportMaxBridgeLength { get; set; } - public float SupportMaxPillarLinkDistance { get; set; } - - - public byte SupportPointsDensityRelative { get; set; } - public float SupportPointsMinimalDistance { get; set; } - - #endregion - - #region Pad - - public bool PadEnable { set; get; } - public float PadWallThickness { set; get; } - public float PadWallHeight { set; get; } - public float PadBrimSize { set; get; } - public float PadMaxMergeDistance { set; get; } - public float PadWallSlope { set; get; } - //public float PadAroundObject { set; get; } - public bool PadAroundObjectEverywhere { set; get; } - public float PadObjectGap { set; get; } - public float PadObjectConnectorStride { set; get; } - public float PadObjectConnectorWidth { set; get; } - public float PadObjectConnectorPenetration { set; get; } - #endregion - - #region Hollowing - public bool HollowingEnable { set; get; } - public float HollowingMinThickness { set; get; } - public float HollowingQuality { set; get; } - public float HollowingClosingDistance { set; get; } - #endregion - - #region Advanced - public float SliceClosingRadius { set; get; } - #endregion - - #region Output - public string OutputFilenameFormat { set; get; } - #endregion - - #region Dependencies - public string CompatiblePrintsCondition { set; get; } - #endregion - - #region Overrides - public override string ToString() - { - return $"{nameof(SlaPrintSettingsId)}: {SlaPrintSettingsId}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(FadedLayers)}: {FadedLayers}, {nameof(SupportsEnable)}: {SupportsEnable}, {nameof(SupportHeadFrontDiameter)}: {SupportHeadFrontDiameter}, {nameof(SupportHeadPenetration)}: {SupportHeadPenetration}, {nameof(SupportHeadWidth)}: {SupportHeadWidth}, {nameof(SupportPillarWideningFactor)}: {SupportPillarWideningFactor}, {nameof(SupportPillarDiameter)}: {SupportPillarDiameter}, {nameof(SupportMaxBridgesOnPillar)}: {SupportMaxBridgesOnPillar}, {nameof(SupportPillarConnectionMode)}: {SupportPillarConnectionMode}, {nameof(SupportBuildplateOnly)}: {SupportBuildplateOnly}, {nameof(SupportBaseDiameter)}: {SupportBaseDiameter}, {nameof(SupportBaseHeight)}: {SupportBaseHeight}, {nameof(SupportBaseSafetyDistance)}: {SupportBaseSafetyDistance}, {nameof(PadAroundObject)}: {PadAroundObject}, {nameof(SupportObjectElevation)}: {SupportObjectElevation}, {nameof(SupportCriticalAngle)}: {SupportCriticalAngle}, {nameof(SupportMaxBridgeLength)}: {SupportMaxBridgeLength}, {nameof(SupportMaxPillarLinkDistance)}: {SupportMaxPillarLinkDistance}, {nameof(SupportPointsDensityRelative)}: {SupportPointsDensityRelative}, {nameof(SupportPointsMinimalDistance)}: {SupportPointsMinimalDistance}, {nameof(PadEnable)}: {PadEnable}, {nameof(PadWallThickness)}: {PadWallThickness}, {nameof(PadWallHeight)}: {PadWallHeight}, {nameof(PadBrimSize)}: {PadBrimSize}, {nameof(PadMaxMergeDistance)}: {PadMaxMergeDistance}, {nameof(PadWallSlope)}: {PadWallSlope}, {nameof(PadAroundObjectEverywhere)}: {PadAroundObjectEverywhere}, {nameof(PadObjectGap)}: {PadObjectGap}, {nameof(PadObjectConnectorStride)}: {PadObjectConnectorStride}, {nameof(PadObjectConnectorWidth)}: {PadObjectConnectorWidth}, {nameof(PadObjectConnectorPenetration)}: {PadObjectConnectorPenetration}, {nameof(HollowingEnable)}: {HollowingEnable}, {nameof(HollowingMinThickness)}: {HollowingMinThickness}, {nameof(HollowingQuality)}: {HollowingQuality}, {nameof(HollowingClosingDistance)}: {HollowingClosingDistance}, {nameof(SliceClosingRadius)}: {SliceClosingRadius}, {nameof(OutputFilenameFormat)}: {OutputFilenameFormat}, {nameof(CompatiblePrintsCondition)}: {CompatiblePrintsCondition}"; - } - #endregion - } - - #endregion - - #region OutputConfig - - public class OutputConfig - { - public string Action { get; set; } - public string JobDir { get; set; } - public float ExpTime { get; set; } - public float ExpTimeFirst { get; set; } - public string FileCreationTimestamp { get; set; } - public float LayerHeight { get; set; } - public string MaterialName { get; set; } - public byte NumFade { get; set; } - public ushort NumFast { get; set; } - public byte NumSlow { get; set; } - public string PrintProfile { get; set; } - public float PrintTime { get; set; } - public string PrinterModel { get; set; } - public string PrinterProfile { get; set; } - public string PrinterVariant { get; set; } - public string PrusaSlicerVersion { get; set; } - public float UsedMaterial { get; set; } - - public override string ToString() - { - return $"{nameof(Action)}: {Action}, {nameof(JobDir)}: {JobDir}, {nameof(ExpTime)}: {ExpTime}, {nameof(ExpTimeFirst)}: {ExpTimeFirst}, {nameof(FileCreationTimestamp)}: {FileCreationTimestamp}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(MaterialName)}: {MaterialName}, {nameof(NumFade)}: {NumFade}, {nameof(NumFast)}: {NumFast}, {nameof(NumSlow)}: {NumSlow}, {nameof(PrintProfile)}: {PrintProfile}, {nameof(PrintTime)}: {PrintTime}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterProfile)}: {PrinterProfile}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrusaSlicerVersion)}: {PrusaSlicerVersion}, {nameof(UsedMaterial)}: {UsedMaterial}"; - } - } - - #endregion - - #endregion - - #region Properties - public Printer PrinterSettings { get; private set; } - - public Material MaterialSettings { get; private set; } - - public Print PrintSettings { get; private set; } - - public OutputConfig OutputConfigSettings { get; private set; } - - public Statistics Statistics { get; } = new Statistics(); - - - public override FileFormatType FileType => FileFormatType.Archive; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("sl1", "PrusaSlicer SL1 Files") - }; - - public override Type[] ConvertToFormats { get; } = - { - typeof(ChituboxFile), - typeof(ChituboxZipFile), - typeof(PWSFile), - typeof(PHZFile), - typeof(ZCodexFile), - typeof(CWSFile), - }; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - }; - - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = { new System.Drawing.Size(400, 400), new System.Drawing.Size(800, 480) }; - //public override Image<Rgba32>[] Thumbnails { get; set; } - - public override uint ResolutionX => PrinterSettings.DisplayPixelsX; - - public override uint ResolutionY => PrinterSettings.DisplayPixelsY; - public override byte AntiAliasing => (byte) (PrinterSettings.GammaCorrection > 0 ? LookupCustomValue(Keyword_AntiAliasing, 4) : 1); - - public override float LayerHeight => OutputConfigSettings.LayerHeight; - - public override ushort InitialLayerCount => OutputConfigSettings.NumFade; - - public override float InitialExposureTime => OutputConfigSettings.ExpTimeFirst; - - public override float LayerExposureTime => OutputConfigSettings.ExpTime; - - public override float LiftHeight { get; } = 0; - - public override float LiftSpeed { get; } = 0; - - public override float RetractSpeed { get; } = 0; - - public override float PrintTime => OutputConfigSettings.PrintTime; - - public override float UsedMaterial => OutputConfigSettings.UsedMaterial; - - public override float MaterialCost => (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.BottleCost / MaterialSettings.BottleVolume, 2); - - public override string MaterialName => OutputConfigSettings.MaterialName; - - public override string MachineName => PrinterSettings.PrinterSettingsId; - - public override object[] Configs => new object[] { PrinterSettings, MaterialSettings, PrintSettings, OutputConfigSettings }; - #endregion - - #region Overrides - public override string ToString() - { - return $"{nameof(FileFullPath)}: {FileFullPath}, {nameof(MaterialSettings)}: {MaterialSettings}, {nameof(PrintSettings)}: {PrintSettings}, {nameof(OutputConfigSettings)}: {OutputConfigSettings}, {nameof(Statistics)}: {Statistics}, {nameof(LayerCount)}: {LayerCount}, {nameof(TotalHeight)}: {TotalHeight}"; - } - - #endregion - - #region Contructors - public SL1File() { } - public SL1File(string fileFullPath) - { - Decode(fileFullPath); - } - #endregion - - #region Static Methods - public static string IniKeyToMemberName(string keyName) - { - string memberName = string.Empty; - string[] objs = keyName.Split('_'); - return objs.Aggregate(memberName, (current, obj) => current + obj.FirstCharToUpper()); - } - - public static string MemberNameToIniKey(string memberName) - { - string iniKey = char.ToLowerInvariant(memberName[0]).ToString(); - for (var i = 1; i < memberName.Length; i++) - { - iniKey += char.IsUpper(memberName[i]) - ? $"_{char.ToLowerInvariant(memberName[i])}" - : memberName[i].ToString(); - } - - - if (iniKey.EndsWith("_")) - iniKey.Remove(iniKey.Length - 1); - - return iniKey; - } - - - #endregion - - #region Methods - public override void Clear() - { - base.Clear(); - Statistics.Clear(); - } - - public override void Encode(string fileFullPath) - { - throw new NotImplementedException(); - } - - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - FileFullPath = fileFullPath; - - PrinterSettings = new Printer(); - MaterialSettings = new Material(); - PrintSettings = new Print(); - OutputConfigSettings = new OutputConfig(); - - Statistics.ExecutionTime.Restart(); - - using (var inputFile = ZipFile.OpenRead(FileFullPath)) - { - - foreach (ZipArchiveEntry entity in inputFile.Entries) - { - if (!entity.Name.EndsWith(".ini")) continue; - using (StreamReader streamReader = new StreamReader(entity.Open())) - { - string line = null; - while ((line = streamReader.ReadLine()) != null) - { - string[] keyValue = line.Split(new[] {'='}, 2); - if (keyValue.Length < 2) continue; - keyValue[0] = keyValue[0].Trim(); - keyValue[1] = keyValue[1].Trim(); - - var fieldName = IniKeyToMemberName(keyValue[0]); - bool foundMember = false; - - foreach (var obj in Configs) - { - var attribute = obj.GetType().GetProperty(fieldName); - if (ReferenceEquals(attribute, null)) continue; - Helpers.SetPropertyValue(attribute, obj, keyValue[1]); - Statistics.ImplementedKeys.Add(keyValue[0]); - foundMember = true; - } - - if (!foundMember) - { - Statistics.MissingKeys.Add(keyValue[0]); - } - } - } - } - - LayerManager = new LayerManager((uint) (OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast)); - - foreach (ZipArchiveEntry entity in inputFile.Entries) - { - if (!entity.Name.EndsWith(".png")) continue; - if (entity.Name.StartsWith("thumbnail")) - { - using (Stream stream = entity.Open()) - { - var image = Image.Load<Rgba32>(stream); - byte thumbnailIndex = - (byte) (image.Width == ThumbnailsOriginalSize[(int) FileThumbnailSize.Small].Width && - image.Height == ThumbnailsOriginalSize[(int) FileThumbnailSize.Small].Height - ? FileThumbnailSize.Small - : FileThumbnailSize.Large); - Thumbnails[thumbnailIndex] = image; - stream.Close(); - } - - //thumbnailIndex++; - - continue; - } - - // - .png - 5 numbers - string layerStr = entity.Name.Substring(entity.Name.Length - 4 - 5, 5); - uint iLayer = uint.Parse(layerStr); - LayerManager[iLayer] = new Layer(iLayer, entity.Open(), entity.Name); - } - } - - Statistics.ExecutionTime.Stop(); - - Debug.WriteLine(Statistics); - } - - /*public override Image<L8> GetLayerImage(uint layerIndex) - { - //Stopwatch sw = Stopwatch.StartNew(); - var image = Image.Load<L8>(DecompressLayer(Layers[layerIndex])); - //Debug.WriteLine(sw.ElapsedMilliseconds); - - return layerIndex >= LayerCount ? null : image; - //return layerIndex >= LayerCount ? null : Image.Load<L8>(LayerEntries[(int)layerIndex].Open()); - //return layerIndex >= LayerCount ? null : Image.Load<L8>(DecompressLayer(Layers[layerIndex])); - }*/ - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - PrintSettings.FadedLayers = - OutputConfigSettings.NumFade = value.Convert<byte>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - MaterialSettings.InitialExposureTime = - OutputConfigSettings.ExpTimeFirst = value.Convert<float>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - MaterialSettings.ExposureTime = - OutputConfigSettings.ExpTime = value.Convert<float>(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - - } - - using (var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update)) - { - - //InputFile.CreateEntry("Modified"); - using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("config.ini", string.Empty).Open())) - { - var properties = OutputConfigSettings.GetType() - .GetProperties(BindingFlags.Public | BindingFlags.Instance); - - foreach (var property in properties) - { - var name = char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1); - tw.WriteLine($"{name} = {property.GetValue(OutputConfigSettings)}"); - } - - tw.Close(); - } - - using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("prusaslicer.ini", string.Empty).Open())) - { - foreach (var config in Configs) - { - if (ReferenceEquals(config, OutputConfigSettings)) - continue; - - var properties = config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - - foreach (var property in properties) - { - tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}"); - } - } - - tw.Close(); - } - - foreach (var layer in this) - { - if (!layer.IsModified) continue; - outputFile.PutFileContent(layer.Filename, layer.RawData); - layer.IsModified = false; - } - } - - Decode(FileFullPath); - - } - - public override bool Convert(Type to, string fileFullPath) - { - if (!IsValid) return false; - - if (to == typeof(ChituboxFile)) - { - ChituboxFile defaultFormat = (ChituboxFile)FindByType(typeof(ChituboxFile)); - ChituboxFile file = new ChituboxFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Version = 2, - BedSizeX = PrinterSettings.DisplayWidth, - BedSizeY = PrinterSettings.DisplayHeight, - BedSizeZ = PrinterSettings.MaxPrintHeight, - OverallHeightMilimeter = TotalHeight, - BottomExposureSeconds = InitialExposureTime, - BottomLayersCount = InitialLayerCount, - BottomLightPWM = LookupCustomValue<ushort>(Keyword_BottomLightPWM, defaultFormat.HeaderSettings.BottomLightPWM), - LayerCount = LayerCount, - LayerExposureSeconds = LayerExposureTime, - LayerHeightMilimeter = LayerHeight, - LayerOffTime = LookupCustomValue<float>(Keyword_LayerOffTime, defaultFormat.HeaderSettings.LayerOffTime), - LightPWM = LookupCustomValue<ushort>(Keyword_LightPWM, defaultFormat.HeaderSettings.LightPWM), - PrintTime = (uint) OutputConfigSettings.PrintTime, - ProjectorType = PrinterSettings.DisplayMirrorX ? 1u : 0u, - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - AntiAliasLevel = ValidateAntiAliasingLevel() - }, - PrintParametersSettings = - { - BottomLayerCount = PrintSettings.FadedLayers, - BottomLiftHeight = LookupCustomValue<float>(Keyword_BottomLiftHeight, - defaultFormat.PrintParametersSettings.BottomLiftHeight), - BottomLiftSpeed = LookupCustomValue<float>(Keyword_BottomLiftSpeed, - defaultFormat.PrintParametersSettings.BottomLiftSpeed), - BottomLightOffDelay = LookupCustomValue<float>(Keyword_BottomLightOffDelay, - defaultFormat.PrintParametersSettings.BottomLightOffDelay), - CostDollars = MaterialCost, - LiftHeight = LookupCustomValue<float>(Keyword_LiftHeight, - defaultFormat.PrintParametersSettings.LiftHeight), - LiftSpeed = LookupCustomValue<float>(Keyword_LiftSpeed, - defaultFormat.PrintParametersSettings.LiftSpeed), - LightOffDelay = LookupCustomValue<float>(Keyword_LightOffDelay, - defaultFormat.PrintParametersSettings.LightOffDelay), - RetractSpeed = LookupCustomValue<float>(Keyword_RetractSpeed, - defaultFormat.PrintParametersSettings.RetractSpeed), - VolumeMl = UsedMaterial, - WeightG = (float) Math.Round( - OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 2) - }, - SlicerInfoSettings = {MachineName = MachineName, MachineNameSize = (uint) MachineName.Length} - }; - - - if (LookupCustomValue<bool>("FLIP_XY", false, true)) - { - file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsY; - file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsX; - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ChituboxZipFile)) - { - ChituboxZipFile defaultFormat = (ChituboxZipFile)FindByType(typeof(ChituboxZipFile)); - ChituboxZipFile file = new ChituboxZipFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Filename = Path.GetFileName(FileFullPath), - - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - MachineX = PrinterSettings.DisplayWidth, - MachineY = PrinterSettings.DisplayHeight, - MachineZ = PrinterSettings.MaxPrintHeight, - MachineType = MachineName, - ProjectType = PrinterSettings.DisplayMirrorX ? "LCD_mirror" : "Normal", - - Resin = MaterialName, - Price = MaterialCost, - Weight = (float) Math.Round(UsedMaterial * MaterialSettings.MaterialDensity, 2), - Volume = UsedMaterial, - Mirror = (byte) (PrinterSettings.DisplayMirrorX ? 1 : 0), - - - BottomLayerLiftHeight = LookupCustomValue<float>(Keyword_BottomLiftHeight, defaultFormat.HeaderSettings.BottomLayerLiftHeight), - LayerLiftHeight = LookupCustomValue<float>(Keyword_LiftHeight, defaultFormat.HeaderSettings.LayerLiftHeight), - BottomLayerLiftSpeed = LookupCustomValue<float>(Keyword_BottomLiftSpeed, defaultFormat.HeaderSettings.BottomLayerLiftSpeed), - LayerLiftSpeed = LookupCustomValue<float>(Keyword_LiftSpeed, defaultFormat.HeaderSettings.LayerLiftSpeed), - RetractSpeed = LookupCustomValue<float>(Keyword_RetractSpeed, defaultFormat.HeaderSettings.RetractSpeed), - BottomLayCount = InitialLayerCount, - BottomLayerCount = InitialLayerCount, - BottomLightOffTime = LookupCustomValue<float>(Keyword_BottomLightOffDelay, defaultFormat.HeaderSettings.BottomLightOffTime), - LayerLightOffTime = LookupCustomValue<float>(Keyword_LightOffDelay, defaultFormat.HeaderSettings.LayerLightOffTime), - BottomLayExposureTime = InitialExposureTime, - BottomLayerExposureTime = InitialExposureTime, - LayerExposureTime = LayerExposureTime, - LayerHeight = LayerHeight, - LayerCount = LayerCount, - AntiAliasing = ValidateAntiAliasingLevel(), - BottomLightPWM = LookupCustomValue<byte>(Keyword_BottomLightPWM, defaultFormat.HeaderSettings.BottomLightPWM), - LayerLightPWM = LookupCustomValue<byte>(Keyword_LightPWM, defaultFormat.HeaderSettings.LayerLightPWM), - - EstimatedPrintTime = PrintTime - }, - }; - - - if (LookupCustomValue<bool>("FLIP_XY", false, true)) - { - file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsY; - file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsX; - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(PWSFile)) - { - PWSFile defaultFormat = (PWSFile)FindByType(typeof(PWSFile)); - PWSFile file = new PWSFile - { - LayerManager = LayerManager, - HeaderSettings = - { - ResolutionX = ResolutionX, - ResolutionY = ResolutionY, - LayerHeight = LayerHeight, - LayerExposureTime = LayerExposureTime, - LiftHeight = LookupCustomValue<float>(Keyword_LiftHeight, defaultFormat.HeaderSettings.LiftHeight), - LiftSpeed = LookupCustomValue<float>(Keyword_LiftSpeed, defaultFormat.HeaderSettings.LiftSpeed) / 60, - RetractSpeed = LookupCustomValue<float>(Keyword_RetractSpeed, defaultFormat.HeaderSettings.RetractSpeed) / 60, - LayerOffTime = LookupCustomValue<float>(Keyword_LayerOffTime, defaultFormat.HeaderSettings.LayerOffTime), - BottomLayersCount = InitialLayerCount, - BottomExposureSeconds = InitialExposureTime, - Price = MaterialCost, - Volume = UsedMaterial, - Weight = (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 2), - AntiAliasing = ValidateAntiAliasingLevel() - } - }; - - - if (LookupCustomValue<bool>("FLIP_XY", false, true)) - { - file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsY; - file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsX; - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(PHZFile)) - { - PHZFile defaultFormat = (PHZFile)FindByType(typeof(PHZFile)); - PHZFile file = new PHZFile - { - LayerManager = LayerManager, - HeaderSettings = - { - Version = 2, - BedSizeX = PrinterSettings.DisplayWidth, - BedSizeY = PrinterSettings.DisplayHeight, - BedSizeZ = PrinterSettings.MaxPrintHeight, - OverallHeightMilimeter = TotalHeight, - BottomExposureSeconds = MaterialSettings.InitialExposureTime, - BottomLayersCount = PrintSettings.FadedLayers, - BottomLightPWM = LookupCustomValue<ushort>(Keyword_BottomLightPWM, defaultFormat.HeaderSettings.BottomLightPWM), - LayerCount = LayerCount, - LayerExposureSeconds = MaterialSettings.ExposureTime, - LayerHeightMilimeter = PrintSettings.LayerHeight, - LayerOffTime = LookupCustomValue<float>(Keyword_LayerOffTime, defaultFormat.HeaderSettings.LayerOffTime), - LightPWM = LookupCustomValue<ushort>(Keyword_LightPWM, defaultFormat.HeaderSettings.LightPWM), - PrintTime = (uint) OutputConfigSettings.PrintTime, - ProjectorType = PrinterSettings.DisplayMirrorX ? 1u : 0u, - ResolutionX = PrinterSettings.DisplayPixelsX, - ResolutionY = PrinterSettings.DisplayPixelsY, - BottomLayerCount = PrintSettings.FadedLayers, - BottomLiftHeight = LookupCustomValue<float>(Keyword_BottomLiftHeight, defaultFormat.HeaderSettings.BottomLiftHeight), - BottomLiftSpeed = LookupCustomValue<float>(Keyword_BottomLiftSpeed, defaultFormat.HeaderSettings.BottomLiftSpeed), - BottomLightOffDelay = LookupCustomValue<float>(Keyword_BottomLightOffDelay, defaultFormat.HeaderSettings.BottomLightOffDelay), - CostDollars = MaterialCost, - LiftHeight = LookupCustomValue<float>(Keyword_LiftHeight, defaultFormat.HeaderSettings.LiftHeight), - LiftSpeed = LookupCustomValue<float>(Keyword_LiftSpeed, defaultFormat.HeaderSettings.LiftSpeed), - RetractSpeed = LookupCustomValue<float>(Keyword_RetractSpeed, defaultFormat.HeaderSettings.RetractSpeed), - VolumeMl = OutputConfigSettings.UsedMaterial, - WeightG = (float)Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 2), - MachineName = MachineName, - MachineNameSize = (uint)MachineName.Length, - AntiAliasLevelInfo = ValidateAntiAliasingLevel() - } - }; - - if (LookupCustomValue<bool>("FLIP_XY", false, true)) - { - file.HeaderSettings.ResolutionX = ResolutionY; - file.HeaderSettings.ResolutionY = ResolutionX; - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - - return true; - } - - if (to == typeof(ZCodexFile)) - { - ZCodexFile defaultFormat = (ZCodexFile)FindByType(typeof(ZCodexFile)); - TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime); - ZCodexFile file = new ZCodexFile - { - ResinMetadataSettings = new ZCodexFile.ResinMetadata - { - MaterialId = 2, - Material = MaterialName, - AdditionalSupportLayerTime = 0, - BottomLayersNumber = InitialLayerCount, - BottomLayersTime = (uint)(InitialExposureTime*1000), - LayerTime = (uint)(LayerExposureTime * 1000), - DisableSettingsChanges = false, - LayerThickness = LayerHeight, - PrintTime = (uint)PrintTime, - TotalLayersCount = LayerCount, - TotalMaterialVolumeUsed = UsedMaterial, - TotalMaterialWeightUsed = UsedMaterial, - }, - UserSettings = new ZCodexFile.UserSettingsdata - { - Printer = MachineName, - BottomLayersCount = InitialLayerCount, - PrintTime = $"{ts.Hours}h {ts.Minutes}m", - LayerExposureTime = (uint)(LayerExposureTime * 1000), - BottomLayerExposureTime = (uint)(InitialExposureTime * 1000), - MaterialId = 2, - LayerThickness = $"{LayerHeight} mm", - AntiAliasing = (byte) (ValidateAntiAliasingLevel() > 1 ? 1 : 0), - CrossSupportEnabled = 1, - ExposureOffTime = LookupCustomValue<uint>(Keyword_LayerOffTime, defaultFormat.UserSettings.ExposureOffTime) *1000, - HollowEnabled = PrintSettings.HollowingEnable ? (byte)1 : (byte)0, - HollowThickness = PrintSettings.HollowingMinThickness, - InfillDensity = 0, - IsAdvanced = 0, - MaterialType = MaterialName, - MaterialVolume = UsedMaterial, - MaxLayer = LayerCount-1, - ModelLiftEnabled = PrintSettings.SupportObjectElevation > 0 ? (byte)1 : (byte)0, - ModelLiftHeight = PrintSettings.SupportObjectElevation, - RaftEnabled = PrintSettings.SupportBaseHeight > 0 ? (byte)1 : (byte)0, - RaftHeight = PrintSettings.SupportBaseHeight, - RaftOffset = 0, - SupportAdditionalExposureEnabled = 0, - SupportAdditionalExposureTime = 0, - XCorrection = PrinterSettings.AbsoluteCorrection, - YCorrection = PrinterSettings.AbsoluteCorrection, - ZLiftDistance = (float)Math.Round(LookupCustomValue<float>(Keyword_LiftHeight, defaultFormat.UserSettings.ZLiftDistance), 2), - ZLiftFeedRate = (float)Math.Round(LookupCustomValue<float>(Keyword_LiftSpeed, defaultFormat.UserSettings.ZLiftFeedRate), 2), - ZLiftRetractRate = (float)Math.Round(LookupCustomValue<float>(Keyword_RetractSpeed, defaultFormat.UserSettings.ZLiftRetractRate), 2), - }, - ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata - { - PrintTime = (uint)PrintTime, - PrinterName = MachineName, - Materials = new List<ZCodexFile.ZCodeMetadata.MaterialsData> - { - new ZCodexFile.ZCodeMetadata.MaterialsData - { - Name = MaterialName, - ExtruderType = "MAIN", - Id = 0, - Usage = 0, - Temperature = 0 - } - }, - }, - LayerManager = LayerManager - }; - - float usedMaterial = UsedMaterial / LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData - { - Layer = layerIndex, - UsedMaterialVolume = usedMaterial - }); - } - - file.SetThumbnails(Thumbnails); - file.Encode(fileFullPath); - return true; - } - - if (to == typeof(CWSFile)) - { - CWSFile defaultFormat = (CWSFile)FindByType(typeof(CWSFile)); - CWSFile file = new CWSFile - { - LayerManager = LayerManager - }; - - file.SliceSettings.Xppm = file.OutputSettings.PixPermmX = (float) Math.Round(LookupCustomValue<float>("Xppm", file.SliceSettings.Xppm), 3); - file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float) Math.Round(LookupCustomValue<float>("Yppm", file.SliceSettings.Xppm), 3); - file.SliceSettings.Xres = file.OutputSettings.XResolution = (ushort)ResolutionX; - file.SliceSettings.Yres = file.OutputSettings.YResolution = (ushort)ResolutionY; - file.SliceSettings.Thickness = file.OutputSettings.LayerThickness = LayerHeight; - file.SliceSettings.LayersNum = file.OutputSettings.LayersNum = LayerCount; - file.SliceSettings.HeadLayersNum = file.OutputSettings.NumberBottomLayers = InitialLayerCount; - file.SliceSettings.LayersExpoMs = file.OutputSettings.LayerTime = (uint) LayerExposureTime * 1000; - file.SliceSettings.HeadLayersExpoMs = file.OutputSettings.BottomLayersTime = (uint) InitialExposureTime * 1000; - file.SliceSettings.WaitBeforeExpoMs = LookupCustomValue<uint>("WaitBeforeExpoMs", file.SliceSettings.WaitBeforeExpoMs); - file.SliceSettings.LiftDistance = file.OutputSettings.LiftDistance = (float) Math.Round(LookupCustomValue<float>(Keyword_LiftHeight, file.SliceSettings.LiftDistance), 2); - file.SliceSettings.LiftUpSpeed = file.OutputSettings.ZLiftFeedRate = file.OutputSettings.ZBottomLiftFeedRate = (float) Math.Round(LookupCustomValue<float>(Keyword_LiftSpeed, file.SliceSettings.LiftUpSpeed), 2); - file.SliceSettings.LiftDownSpeed = file.OutputSettings.ZLiftRetractRate = (float) Math.Round(LookupCustomValue<float>(Keyword_RetractSpeed, file.SliceSettings.LiftDownSpeed), 2); - file.SliceSettings.LiftWhenFinished = LookupCustomValue<byte>("LiftWhenFinished", file.SliceSettings.LiftWhenFinished); - - file.OutputSettings.BlankingLayerTime = LookupCustomValue<uint>("BlankingLayerTime", file.OutputSettings.BlankingLayerTime); - //file.OutputSettings.RenderOutlines = false; - //file.OutputSettings.OutlineWidthInset = 0; - //file.OutputSettings.OutlineWidthOutset = 0; - file.OutputSettings.RenderOutlines = false; - //file.OutputSettings.TiltValue = 0; - //file.OutputSettings.UseMainliftGCodeTab = false; - //file.OutputSettings.AntiAliasing = 0; - //file.OutputSettings.AntiAliasingValue = 0; - file.OutputSettings.FlipX = PrinterSettings.DisplayMirrorX; - file.OutputSettings.FlipY = PrinterSettings.DisplayMirrorY; - file.OutputSettings.AntiAliasingValue = ValidateAntiAliasingLevel(); - file.OutputSettings.AntiAliasing = file.OutputSettings.AntiAliasingValue > 1; - - - - if (LookupCustomValue<bool>("FLIP_XY", false, true)) - { - file.SliceSettings.Xres = file.OutputSettings.XResolution = (ushort) ResolutionY; - file.SliceSettings.Yres = file.OutputSettings.YResolution = (ushort) ResolutionX; - } - - file.Encode(fileFullPath); - - return true; - } - - return false; - } - - public T LookupCustomValue<T>(string name, T defaultValue, bool existsOnly = false) - { - if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue; - string result = string.Empty; - if(!existsOnly) - name += '_'; - - int index = PrinterSettings.PrinterNotes.IndexOf(name, StringComparison.Ordinal); - int startIndex = index + name.Length; - - if (index < 0 || PrinterSettings.PrinterNotes.Length < startIndex) return defaultValue; - if (existsOnly) return "true".Convert<T>(); - for (int i = startIndex; i < PrinterSettings.PrinterNotes.Length; i++) - { - char c = PrinterSettings.PrinterNotes[i]; - if (!char.IsLetterOrDigit(c) && c != '.') - { - break; - } - - result += c; - } - - return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>(); - } - - #endregion - } -} diff --git a/UVtools.Parser/Statistics.cs b/UVtools.Parser/Statistics.cs deleted file mode 100644 index aa4fbe9..0000000 --- a/UVtools.Parser/Statistics.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace UVtools.Parser -{ - public class Statistics - { - #region Properties - - public List<string> ImplementedKeys { get; } = new List<string>(); - public List<string> MissingKeys { get; } = new List<string>(); - public ushort TotalKeys => (ushort)(ImplementedKeys.Count + MissingKeys.Count); - - public Stopwatch ExecutionTime { get; } = new Stopwatch(); - #endregion - - #region Overrides - public override string ToString() - { - string message = $"{nameof(ImplementedKeys)}: {ImplementedKeys.Count}, {nameof(MissingKeys)}: {MissingKeys.Count}, {nameof(TotalKeys)}: {TotalKeys}, {nameof(ExecutionTime)}: {ExecutionTime.ElapsedMilliseconds}ms"; - message = MissingKeys.Aggregate(message, (current, missingKey) => current + ("\n" + missingKey)); - return message; - } - - #endregion - - #region Methods - - public void Clear() - { - ImplementedKeys.Clear(); - MissingKeys.Clear(); - ExecutionTime.Reset(); - } - #endregion - } -} diff --git a/UVtools.Parser/UVtools.Parser.csproj b/UVtools.Parser/UVtools.Parser.csproj deleted file mode 100644 index 63e325a..0000000 --- a/UVtools.Parser/UVtools.Parser.csproj +++ /dev/null @@ -1,34 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> - <Authors>Tiago Conceição</Authors> - <Company>PTRTECH</Company> - <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> - <PackageIcon></PackageIcon> - <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> - <AssemblyVersion>0.5.2.2</AssemblyVersion> - <FileVersion>0.5.2.2</FileVersion> - <Version>0.5.2.2</Version> - <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> - <PackageId>UVtools.Parser</PackageId> - <PackageLicenseFile>LICENSE</PackageLicenseFile> - <Product>UVtools.Parser</Product> - <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> - </PropertyGroup> - - <ItemGroup> - <None Include="..\LICENSE" Link="LICENSE" /> - <None Include="..\LICENSE"> - <Pack>True</Pack> - <PackagePath></PackagePath> - </None> - </ItemGroup> - - <ItemGroup> - <PackageReference Include="BinarySerializer" Version="8.5.1" /> - <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> - <PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-rc0003" /> - </ItemGroup> - -</Project> diff --git a/UVtools.Parser/ZCodexFile.cs b/UVtools.Parser/ZCodexFile.cs deleted file mode 100644 index a7409f2..0000000 --- a/UVtools.Parser/ZCodexFile.cs +++ /dev/null @@ -1,437 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Text; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using UVtools.Parser.Extensions; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace UVtools.Parser -{ - public class ZCodexFile : FileFormat - { - #region Constants - - private const string GCodeStart = "G28\nG21\nG91\nM17\n"; - private const string GCodeKeywordSlice = "<Slice>"; - private const string GCodeKeywordDelayBlank = "<Delay_blank>"; - private const string GCodeKeywordDelayModel = "<Delay_model>"; - private const string GCodeKeywordDelaySupportPart = "<Delay_support_part>"; - private const string GCodeKeywordDelaySupportFull = "<Delay_support_full>"; - private const string FolderImages = "ResinSlicesData"; - private const string FolderImageName = "Slice"; - #endregion - - #region Sub Classes - - public class ResinMetadata - { - public class LayerData - { - public uint Layer { get; set; } - public float UsedMaterialVolume { get; set; } - - } - - public string Guid { get; set; } = "07452AC2-7494-4576-BA60-BFEA8815F917"; - public string Material { get; set; } - public uint MaterialId { get; set; } - public float LayerThickness { get; set; } - public uint PrintTime { get; set; } - public uint LayerTime { get; set; } - public uint BottomLayersTime { get; set; } - public uint AdditionalSupportLayerTime { get; set; } - public ushort BottomLayersNumber { get; set; } - public uint BlankingLayerTime { get; set; } - public float TotalMaterialVolumeUsed { get; set; } - public float TotalMaterialWeightUsed { get; set; } - public uint TotalLayersCount { get; set; } - public bool DisableSettingsChanges { get; set; } - - public List<LayerData> Layers { get; set; } = new List<LayerData>(); - } - - public class UserSettingsdata - { - public uint MaxLayer { get; set; } - public string PrintTime { get; set; } - public float MaterialVolume { get; set; } - public byte IsAdvanced { get; set; } - public string Printer { get; set; } = "Zortrax Inkspire"; - public string MaterialType { get; set; } - public uint MaterialId { get; set; } - public string LayerThickness { get; set; } - public byte RaftEnabled { get; set; } - public float RaftHeight { get; set; } - public float RaftOffset { get; set; } - public byte ModelLiftEnabled { get; set; } - public float ModelLiftHeight { get; set; } - public byte CrossSupportEnabled { get; set; } - public uint LayerExposureTime { get; set; } - //public uint LayerThicknessesDisplayTime { get; set; } arr - public uint ExposureOffTime { get; set; } = 5; - public uint BottomLayerExposureTime { get; set; } - public uint BottomLayersCount { get; set; } - public byte SupportAdditionalExposureEnabled { get; set; } - public uint SupportAdditionalExposureTime { get; set; } - public float ZLiftDistance { get; set; } = 5; - public float ZLiftRetractRate { get; set; } = 100; - public float ZLiftFeedRate { get; set; } = 100; - public byte AntiAliasing { get; set; } = 0; - public byte XCorrection { get; set; } - public byte YCorrection { get; set; } - public byte HollowEnabled { get; set; } - public float HollowThickness { get; set; } - public byte InfillDensity { get; set; } - } - - public class ZCodeMetadata - { - public class MaterialsData - { - public string ExtruderType { get; set; } - public uint Id { get; set; } - public string Name { get; set; } - public uint Usage { get; set; } - public uint Temperature { get; set; } - } - - public string ZCodexVersion { get; set; } = "2.0.0.0"; - public string SoftwareVersion { get; set; } = "2.12.2.0"; - public string MinFirmwareVersion { get; set; } = "20013"; - public uint PrinterModelEnumId { get; set; } = 40; - public string PrinterName { get; set; } = "Inkspire"; - public List<MaterialsData> Materials { get; set; } - public byte HeatbedTemperature { get; set; } - public byte ChamberTemperature { get; set; } - public uint CommandCount { get; set; } - public uint PrintTime { get; set; } - public float NozzleDiameter { get; set; } - public string PrintBoundingBox { get; set; } - public string Pauses { get; set; } - public string MaterialUsages { get; set; } - } - - public class LayerData - { - public int SupportLayerFileIndex { get; set; } = -1; - public int LayerFileIndex { get; set; } = -1; - public ZipArchiveEntry SupportLayerEntry { get; set; } - public ZipArchiveEntry LayerEntry { get; set; } - - public bool HaveSupportLayer => !ReferenceEquals(SupportLayerEntry, null); - } - - #endregion - - #region Properties - public ResinMetadata ResinMetadataSettings { get; set; } - public UserSettingsdata UserSettings { get; set; } - public ZCodeMetadata ZCodeMetadataSettings { get; set; } - - public List<LayerData> LayersSettings { get; } = new List<LayerData>(); - - public override FileFormatType FileType => FileFormatType.Archive; - - public override FileExtension[] FileExtensions { get; } = { - new FileExtension("zcodex", "Z-Suite ZCodex Files") - }; - - public override Type[] ConvertToFormats { get; } = null; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = { - PrintParameterModifier.InitialLayerCount, - PrintParameterModifier.InitialExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - - PrintParameterModifier.LiftHeight, - PrintParameterModifier.RetractSpeed, - PrintParameterModifier.LiftSpeed, - }; - - public override byte ThumbnailsCount { get; } = 1; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(320, 180)}; - - public override uint ResolutionX => 1440; - - public override uint ResolutionY => 2560; - public override byte AntiAliasing => UserSettings.AntiAliasing; - - public override float LayerHeight => ResinMetadataSettings.LayerThickness; - - public override ushort InitialLayerCount => ResinMetadataSettings.BottomLayersNumber; - - public override float InitialExposureTime => UserSettings.BottomLayerExposureTime / 1000; - - public override float LayerExposureTime => UserSettings.LayerExposureTime / 1000; - public override float LiftHeight => UserSettings.ZLiftDistance; - - public override float LiftSpeed => UserSettings.ZLiftFeedRate; - - public override float RetractSpeed => UserSettings.ZLiftRetractRate; - - public override float PrintTime => ResinMetadataSettings.PrintTime; - - public override float UsedMaterial => ResinMetadataSettings.TotalMaterialVolumeUsed; - - public override float MaterialCost => 0; - - public override string MaterialName => ResinMetadataSettings.Material; - - public override string MachineName => ZCodeMetadataSettings.PrinterName; - - public override object[] Configs => new[] {(object) ResinMetadataSettings, UserSettings, ZCodeMetadataSettings}; - #endregion - - #region Methods - - public override void Clear() - { - base.Clear(); - LayersSettings.Clear(); - } - - public override void Encode(string fileFullPath) - { - base.Encode(fileFullPath); - using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create)) - { - - outputFile.PutFileContent("ResinMetadata", JsonConvert.SerializeObject(ResinMetadataSettings)); - outputFile.PutFileContent("UserSettingsData", JsonConvert.SerializeObject(UserSettings)); - outputFile.PutFileContent("ZCodeMetadata", JsonConvert.SerializeObject(ZCodeMetadataSettings)); - - if (CreatedThumbnailsCount > 0) - { - using (Stream stream = outputFile.CreateEntry("Preview.png").Open()) - { - Thumbnails[0].Save(stream, Helpers.PngEncoder); - stream.Close(); - } - } - - GCode = new StringBuilder(GCodeStart); - - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}"); - GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}"); - GCode.AppendLine($"G1 Z-{LiftHeight - LayerHeight} F{RetractSpeed}"); - GCode.AppendLine(GCodeKeywordDelayBlank); - GCode.AppendLine("M106 S255"); - GCode.AppendLine(GCodeKeywordDelayModel); - GCode.AppendLine("M106 S0"); - - - var layerimagePath = $"{FolderImages}/{FolderImageName}{layerIndex:D5}.png"; - using (Stream stream = outputFile.CreateEntry(layerimagePath).Open()) - { - //image.Save(stream, Helpers.PngEncoder); - var byteArr = this[layerIndex].RawData; - stream.Write(byteArr, 0, byteArr.Length); - stream.Close(); - } - } - - GCode.AppendLine($"G1 Z40.0 F{UserSettings.ZLiftFeedRate}"); - GCode.AppendLine("M18"); - - outputFile.PutFileContent("ResinGCodeData", GCode.ToString()); - } - } - - public override void Decode(string fileFullPath) - { - base.Decode(fileFullPath); - - FileFullPath = fileFullPath; - using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) - { - var entry = inputFile.GetEntry("ResinMetadata"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("ResinMetadata not found", fileFullPath); - } - - ResinMetadataSettings = Helpers.JsonDeserializeObject<ResinMetadata>(entry.Open()); - - entry = inputFile.GetEntry("UserSettingsData"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("UserSettingsData not found", fileFullPath); - } - - UserSettings = Helpers.JsonDeserializeObject<UserSettingsdata>(entry.Open()); - - entry = inputFile.GetEntry("ZCodeMetadata"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("ZCodeMetadata not found", fileFullPath); - } - - ZCodeMetadataSettings = Helpers.JsonDeserializeObject<ZCodeMetadata>(entry.Open()); - - entry = inputFile.GetEntry("ResinGCodeData"); - if (ReferenceEquals(entry, null)) - { - Clear(); - throw new FileLoadException("ResinGCodeData not found", fileFullPath); - } - - LayerManager = new LayerManager(ResinMetadataSettings.TotalLayersCount); - GCode = new StringBuilder(); - using (TextReader tr = new StreamReader(entry.Open())) - { - string line; - int layerIndex = 0; - int layerFileIndex = 0; - string layerimagePath = null; - while (!ReferenceEquals(line = tr.ReadLine(), null)) - { - GCode.AppendLine(line); - if (line.StartsWith(GCodeKeywordSlice)) - { - layerFileIndex = int.Parse(line.Substring(GCodeKeywordSlice.Length)); - layerimagePath = $"{FolderImages}/{FolderImageName}{layerFileIndex:D5}.png"; - if (LayersSettings.Count - 1 < layerIndex) LayersSettings.Add(new LayerData()); - continue; - } - - if (line.StartsWith(GCodeKeywordDelaySupportPart)) - { - LayersSettings[layerIndex].SupportLayerFileIndex = layerFileIndex; - LayersSettings[layerIndex].SupportLayerEntry = inputFile.GetEntry(layerimagePath); - continue; - } - - if (line.StartsWith(GCodeKeywordDelaySupportFull) || line.StartsWith(GCodeKeywordDelayModel)) - { - LayersSettings[layerIndex].LayerFileIndex = layerFileIndex; - LayersSettings[layerIndex].LayerEntry = inputFile.GetEntry(layerimagePath); - this[layerIndex] = new Layer((uint) layerIndex, LayersSettings[layerIndex].LayerEntry.Open(), LayersSettings[layerIndex].LayerEntry.Name); - layerIndex++; - continue; - } - } - - tr.Close(); - } - - entry = inputFile.GetEntry("Preview.png"); - if (!ReferenceEquals(entry, null)) - { - using (Stream stream = entry.Open()) - { - Thumbnails[0] = Image.Load<Rgba32>(stream); - stream.Close(); - } - } - } - } - - public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) - { - if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) - { - UserSettings.BottomLayersCount = - ResinMetadataSettings.BottomLayersNumber = value.Convert<ushort>(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) - { - ResinMetadataSettings.BottomLayersTime = - UserSettings.BottomLayerExposureTime = value.Convert<uint>()*1000; - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) - { - ResinMetadataSettings.LayerTime = - UserSettings.LayerExposureTime = value.Convert<uint>()*1000; - return true; - } - - if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) - { - UserSettings.ZLiftDistance = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) - { - UserSettings.ZLiftFeedRate = value.Convert<float>(); - UpdateGCode(); - return true; - } - if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) - { - UserSettings.ZLiftRetractRate = value.Convert<float>(); - UpdateGCode(); - return true; - } - - return false; - } - - public override void SaveAs(string filePath = null) - { - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - - } - - using (var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update)) - { - outputFile.PutFileContent("ResinMetadata", JsonConvert.SerializeObject(ResinMetadataSettings)); - outputFile.PutFileContent("UserSettingsData", JsonConvert.SerializeObject(UserSettings)); - outputFile.PutFileContent("ZCodeMetadata", JsonConvert.SerializeObject(ZCodeMetadataSettings)); - outputFile.PutFileContent("ResinGCodeData", GCode.ToString()); - - foreach (var layer in this) - { - if (!layer.IsModified) continue; - outputFile.PutFileContent(layer.Filename, layer.RawData); - layer.IsModified = false; - } - } - - //Decode(FileFullPath); - } - - public override bool Convert(Type to, string fileFullPath) - { - throw new NotImplementedException(); - } - - private void UpdateGCode() - { - var gcode = GCode.ToString(); - gcode = Regex.Replace(gcode, @"Z[+]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", - $"Z{UserSettings.ZLiftDistance} F{UserSettings.ZLiftFeedRate}"); - - gcode = Regex.Replace(gcode, @"Z-[-]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", - $"Z-{UserSettings.ZLiftDistance - LayerHeight} F{UserSettings.ZLiftRetractRate}"); - - GCode.Clear(); - GCode.Append(gcode); - - } - #endregion - } -} diff --git a/UVtools.sln b/UVtools.sln index 6a73697..f048516 100644 --- a/UVtools.sln +++ b/UVtools.sln @@ -5,15 +5,13 @@ VisualStudioVersion = 16.0.29926.136 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UVtools.GUI", "UVtools.GUI\UVtools.GUI.csproj", "{E7389EE3-CF56-464B-9BA1-816B31D1E6FF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.Parser", "UVtools.Parser\UVtools.Parser.csproj", "{53B884CA-E640-4171-8AA2-03935AC076D2}" -EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "UVtools.InstallerMM", "UVtools.InstallerMM\UVtools.InstallerMM.wixproj", "{E53BAA5D-29A8-4287-B3AA-1AFF5A4BDC6C}" EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "UVtools.Installer", "UVtools.Installer\UVtools.Installer.wixproj", "{41224896-08E9-4F22-9E56-5F9D46DEC7D2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.Core", "UVtools.Core\UVtools.Core.csproj", "{7C9927F8-132E-4A37-B894-440E0FD5AA3D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UVtools.Terminal", "UVtools.Console\UVtools.Terminal.csproj", "{36E5877E-6AA6-4368-A9EA-46D7C7C90302}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.Cmd", "UVtools.Cmd\UVtools.Cmd.csproj", "{36E5877E-6AA6-4368-A9EA-46D7C7C90302}" ProjectSection(ProjectDependencies) = postProject {7C9927F8-132E-4A37-B894-440E0FD5AA3D} = {7C9927F8-132E-4A37-B894-440E0FD5AA3D} EndProjectSection @@ -34,14 +32,6 @@ Global {E7389EE3-CF56-464B-9BA1-816B31D1E6FF}.Release|Any CPU.Build.0 = Release|Any CPU {E7389EE3-CF56-464B-9BA1-816B31D1E6FF}.Release|x86.ActiveCfg = Release|Any CPU {E7389EE3-CF56-464B-9BA1-816B31D1E6FF}.Release|x86.Build.0 = Release|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Debug|x86.ActiveCfg = Debug|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Debug|x86.Build.0 = Debug|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Release|Any CPU.Build.0 = Release|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Release|x86.ActiveCfg = Release|Any CPU - {53B884CA-E640-4171-8AA2-03935AC076D2}.Release|x86.Build.0 = Release|Any CPU {E53BAA5D-29A8-4287-B3AA-1AFF5A4BDC6C}.Debug|Any CPU.ActiveCfg = Debug|x86 {E53BAA5D-29A8-4287-B3AA-1AFF5A4BDC6C}.Debug|x86.ActiveCfg = Debug|x86 {E53BAA5D-29A8-4287-B3AA-1AFF5A4BDC6C}.Debug|x86.Build.0 = Debug|x86 |