diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2020-07-04 23:29:58 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2020-07-04 23:29:58 +0300 |
commit | 2c514fe260b7939f372c745b5e600ee877b88e12 (patch) | |
tree | b0c7b92f8a3fbf30c3a983efb17c5ac0ebae0657 | |
parent | 08fe746dac81d2311978427ebf44f06f10601186 (diff) |
v0.6.1.1v0.6.1.1
* (Add) Allow chitubox, phz, pws, pw0 files convert to cws
* (Add) Allow convert between cbddlp, ctb and photon
* (Add) Allow convert between pws and pw0
* (Improvement) Layers can now have modified heights and independent parameters (#9)
* (Improvement) UVtools now generate better gcode and detect the lack of Lift and same z position and optimize the commands
* (Fix) zcodex: Wasn't reporting layer decoding progress
-rw-r--r-- | CHANGELOG.md | 10 | ||||
-rw-r--r-- | UVtools.Cmd/Program.cs | 1 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/CWSFile.cs (renamed from UVtools.Core/CWSFile.cs) | 96 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/ChituboxFile.cs (renamed from UVtools.Core/ChituboxFile.cs) | 139 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/ChituboxZipFile.cs (renamed from UVtools.Core/ChituboxZipFile.cs) | 80 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileExtension.cs (renamed from UVtools.Core/FileExtension.cs) | 2 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs (renamed from UVtools.Core/FileFormat.cs) | 6 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/IFileFormat.cs (renamed from UVtools.Core/IFileFormat.cs) | 3 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/ImageFile.cs (renamed from UVtools.Core/ImageFile.cs) | 4 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/PHZFile.cs (renamed from UVtools.Core/PHZFile.cs) | 67 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/PWSFile.cs (renamed from UVtools.Core/PWSFile.cs) | 96 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/SL1File.cs (renamed from UVtools.Core/SL1File.cs) | 34 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/ZCodexFile.cs (renamed from UVtools.Core/ZCodexFile.cs) | 100 | ||||
-rw-r--r-- | UVtools.Core/Layer/Layer.cs | 692 | ||||
-rw-r--r-- | UVtools.Core/Layer/LayerIssue.cs | 245 | ||||
-rw-r--r-- | UVtools.Core/Layer/LayerManager.cs (renamed from UVtools.Core/LayerManager.cs) | 922 | ||||
-rw-r--r-- | UVtools.Core/MatBytes.cs | 84 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationMove.cs (renamed from UVtools.Core/OperationMove.cs) | 4 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationPattern.cs (renamed from UVtools.Core/OperationPattern.cs) | 2 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationProgress.cs (renamed from UVtools.Core/OperationProgress.cs) | 2 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 7 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmAbout.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmInputBox.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmLoading.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmMutationMove.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmSettings.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Forms/FrmToolPattern.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/FrmMain.Designer.cs | 20 | ||||
-rw-r--r-- | UVtools.GUI/FrmMain.cs | 32 | ||||
-rw-r--r-- | UVtools.GUI/Program.cs | 1 | ||||
-rw-r--r-- | UVtools.GUI/Properties/AssemblyInfo.cs | 4 |
31 files changed, 1547 insertions, 1112 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db33d3..4cc4018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 02/07/2020 - v0.6.1.1 + +* (Add) Allow chitubox, phz, pws, pw0 files convert to cws +* (Add) Allow convert between cbddlp, ctb and photon +* (Add) Allow convert between pws and pw0 +* (Improvement) Layers can now have modified heights and independent parameters (#9) +* (Improvement) UVtools now generate better gcode and detect the lack of Lift and same z position and optimize the commands +* (Fix) zcodex: Wasn't reporting layer decoding progress + + ## 02/07/2020 - v0.6.1.0 * (Add) Thumbnail image can now saved to clipboard diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs index b1224f7..15cbff0 100644 --- a/UVtools.Cmd/Program.cs +++ b/UVtools.Cmd/Program.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using UVtools.Core; +using UVtools.Core.FileFormats; namespace UVtools.Cmd { diff --git a/UVtools.Core/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 4dcfa76..f6a1ddb 100644 --- a/UVtools.Core/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -13,10 +13,11 @@ using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; -using System.Threading.Tasks; +using System.Text.RegularExpressions; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class CWSFile : FileFormat { @@ -290,6 +291,9 @@ namespace UVtools.Core LayerManager = new LayerManager(OutputSettings.LayersNum); + var gcode = GCode.ToString(); + float currentHeight = 0; + foreach (var zipArchiveEntry in inputFile.Entries) { if (!zipArchiveEntry.Name.EndsWith(".png")) continue; @@ -297,8 +301,55 @@ namespace UVtools.Core // - .png - 4 numbers int layerSize = OutputSettings.LayersNum.ToString().Length; string layerStr = zipArchiveEntry.Name.Substring(zipArchiveEntry.Name.Length - 4 - layerSize, layerSize); - uint iLayer = uint.Parse(layerStr); - LayerManager[iLayer] = new Layer(iLayer, zipArchiveEntry.Open(), zipArchiveEntry.Name); + uint layerIndex = uint.Parse(layerStr); + + var startStr = $"{GCodeKeywordSlice} {layerIndex}"; + var stripGcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length); + stripGcode = stripGcode.Substring(0, stripGcode.IndexOf(GCodeKeywordDelay, stripGcode.IndexOf(GCodeKeywordSlice))).Trim(' ', '\n', '\r', '\t'); + //var startCurrPos = stripGcode.Remove(0, ";currPos:".Length); + + /* + * +;<Slice> 0 +M106 S255 +;<Delay> 45000 +M106 S0 +;<Slice> Blank +G1 Z4 F120 +G1 Z-3.9 F120 +;<Delay> 45000 + */ + + var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase); + var exposureTime = Regex.Match(stripGcode, ";<Delay> (\\d+)", RegexOptions.IgnoreCase); + /*var pwm = Regex.Match(stripGcode, "M106 S(\\d+)", RegexOptions.IgnoreCase); + if (layerIndex < InitialLayerCount) + { + OutputSettings.BottomLayerLightPWM = byte.Parse(pwm.Groups[1].Value); + } + else + { + OutputSettings.LayerLightPWM = byte.Parse(pwm.Groups[1].Value); + }*/ + + if (currPos.Success) + { + var nextMatch = currPos.NextMatch(); + if (nextMatch.Success) + { + currentHeight = (float)Math.Round(currentHeight + float.Parse(currPos.Groups[1].Value) + float.Parse(currPos.NextMatch().Groups[1].Value), 2); + } + else + { + currentHeight = (float)Math.Round(currentHeight + float.Parse(currPos.Groups[1].Value), 2); + } + } + + LayerManager[layerIndex] = new Layer(layerIndex, zipArchiveEntry.Open(), zipArchiveEntry.Name) + { + PositionZ = currentHeight, + ExposureTime = float.Parse(exposureTime.Groups[1].Value) / 1000f + }; } } @@ -318,10 +369,19 @@ namespace UVtools.Core public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) { + void UpdateLayers() + { + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + this[layerIndex].ExposureTime = GetInitialLayerValueOrNormal(layerIndex, InitialExposureTime, LayerExposureTime); + } + } + if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) { SliceSettings.HeadLayersNum = OutputSettings.NumberBottomLayers = value.Convert<ushort>(); + UpdateLayers(); UpdateGCode(); return true; } @@ -329,6 +389,7 @@ namespace UVtools.Core { SliceSettings.HeadLayersExpoMs = OutputSettings.BottomLayersTime = value.Convert<uint>()*1000; + UpdateLayers(); UpdateGCode(); return true; } @@ -336,6 +397,7 @@ namespace UVtools.Core { SliceSettings.LayersExpoMs = OutputSettings.LayerTime = value.Convert<uint>() * 1000; + UpdateLayers(); UpdateGCode(); return true; } @@ -453,17 +515,33 @@ namespace UVtools.Core GCode.AppendLine(); GCode.AppendFormat(GCodeStart, Environment.NewLine); + float lastZPosition = 0; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - //Layer layer = this[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($"{GCodeKeywordDelay} {layer.ExposureTime}"); 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)}"); + + if (lastZPosition != layer.PositionZ) + { + if (LiftHeight > 0) + { + GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}"); + GCode.AppendLine($"G1 Z-{LiftHeight - layer.PositionZ + lastZPosition} F{RetractSpeed}"); + } + else + { + GCode.AppendLine($"G1 Z{layer.PositionZ - lastZPosition} F{LiftSpeed}"); + } + } + + GCode.AppendLine($"{GCodeKeywordDelay} {layer.ExposureTime}"); + + lastZPosition = layer.PositionZ; } GCode.AppendFormat(GCodeEnd, Environment.NewLine, SliceSettings.LiftWhenFinished); diff --git a/UVtools.Core/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index a011dd0..2ad10d1 100644 --- a/UVtools.Core/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -19,8 +19,9 @@ using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class ChituboxFile : FileFormat { @@ -508,15 +509,16 @@ namespace UVtools.Core public LayerData(ChituboxFile parent, uint layerIndex) { Parent = parent; - LayerPositionZ = parent.GetHeightFromLayer(layerIndex); + LayerPositionZ = parent[layerIndex].PositionZ; + LayerExposure = parent[layerIndex].ExposureTime; - LayerOffTimeSeconds = layerIndex < parent.HeaderSettings.BottomLayersCount - ? parent.PrintParametersSettings.BottomLightOffDelay - : parent.PrintParametersSettings.LightOffDelay; + LayerOffTimeSeconds = parent.GetInitialLayerValueOrNormal(layerIndex, + parent.PrintParametersSettings.BottomLightOffDelay, + parent.PrintParametersSettings.LightOffDelay); - LayerExposure = layerIndex < parent.HeaderSettings.BottomLayersCount + /*LayerExposure = layerIndex < parent.HeaderSettings.BottomLayersCount ? parent.HeaderSettings.BottomExposureSeconds - : parent.HeaderSettings.LayerExposureSeconds; + : parent.HeaderSettings.LayerExposureSeconds;*/ } public Mat Decode(uint layerIndex, bool consumeData = true) @@ -531,9 +533,6 @@ namespace UVtools.Core public static Mat DecodeCbddlpImage(ChituboxFile parent, uint layerIndex) { - //Mat image = new Mat(new Size((int)parent.HeaderSettings.ResolutionX, (int)parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); - //var bytes = image.GetBytesBlank(); - //var image = EmguExtensions.CreateMat(out var bytes, new Size((int)parent.HeaderSettings.ResolutionX, (int)parent.HeaderSettings.ResolutionY)); var image = new Mat(new Size((int)parent.HeaderSettings.ResolutionX, (int)parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); var span = image.GetPixelSpan<byte>(); @@ -909,10 +908,12 @@ namespace UVtools.Core public override Type[] ConvertToFormats { get; } = { + typeof(ChituboxFile), typeof(ChituboxZipFile), typeof(PWSFile), typeof(PHZFile), typeof(ZCodexFile), + typeof(CWSFile), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = @@ -1013,6 +1014,10 @@ namespace UVtools.Core HeaderSettings.EncryptionKey = (uint)rnd.Next(byte.MaxValue, int.MaxValue); } } + else + { + HeaderSettings.EncryptionKey = 0; + } uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); LayersDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount]; @@ -1099,7 +1104,7 @@ namespace UVtools.Core var layerData = LayersDefinitions[aaIndex, layerIndex]; LayerData layerDataHash = null; - if (!IsCbtFile && HeaderSettings.EncryptionKey == 0) + if (!IsCbtFile /*&& HeaderSettings.EncryptionKey == 0*/) { string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle); if (LayersHash.TryGetValue(hash, out layerDataHash)) @@ -1258,7 +1263,12 @@ namespace UVtools.Core using (var image = LayersDefinitions[0, layerIndex].Decode((uint) layerIndex)) { - this[layerIndex] = new Layer((uint) layerIndex, image); + this[layerIndex] = new Layer((uint) layerIndex, image) + { + PositionZ = LayersDefinitions[0, layerIndex].LayerPositionZ, + ExposureTime = LayersDefinitions[0, layerIndex].LayerExposure + }; + lock (progress.Mutex) { progress++; @@ -1299,8 +1309,10 @@ namespace UVtools.Core 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; + this[layerIndex].ExposureTime = + LayersDefinitions[aaIndex, layerIndex].LayerExposure = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomExposureSeconds, HeaderSettings.LayerExposureSeconds); + + LayersDefinitions[aaIndex, layerIndex].LayerOffTimeSeconds = GetInitialLayerValueOrNormal(layerIndex, PrintParametersSettings.BottomLightOffDelay, HeaderSettings.LayerOffTime); } } } @@ -1427,6 +1439,66 @@ namespace UVtools.Core public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null) { + if (to == typeof(ChituboxFile)) + { + if (Path.GetExtension(FileFullPath).Equals(Path.GetExtension(fileFullPath))) + { + return false; + } + ChituboxFile file = new ChituboxFile + { + LayerManager = LayerManager, + HeaderSettings = + { + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + BedSizeX = HeaderSettings.BedSizeX, + BedSizeY = HeaderSettings.BedSizeY, + BedSizeZ = HeaderSettings.BedSizeZ, + ProjectorType = HeaderSettings.ProjectorType, + LayerCount = LayerCount, + AntiAliasLevel = ValidateAntiAliasingLevel(), + BottomLightPWM = (byte) HeaderSettings.BottomLightPWM, + LightPWM = (byte) HeaderSettings.LightPWM, + LayerOffTime = HeaderSettings.LayerOffTime, + PrintTime = HeaderSettings.PrintTime, + BottomExposureSeconds = HeaderSettings.BottomExposureSeconds, + BottomLayersCount = HeaderSettings.BottomLayersCount, + //EncryptionKey = HeaderSettings.EncryptionKey, + LayerExposureSeconds = HeaderSettings.LayerExposureSeconds, + LayerHeightMilimeter = HeaderSettings.LayerHeightMilimeter, + OverallHeightMilimeter = HeaderSettings.OverallHeightMilimeter, + }, + PrintParametersSettings = + { + LiftSpeed = PrintParametersSettings.LiftSpeed, + LiftHeight = PrintParametersSettings.LiftHeight, + BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed, + RetractSpeed = PrintParametersSettings.RetractSpeed, + BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay, + LightOffDelay = PrintParametersSettings.BottomLightOffDelay, + BottomLayerCount = PrintParametersSettings.BottomLayerCount, + VolumeMl = PrintParametersSettings.VolumeMl, + BottomLiftHeight = PrintParametersSettings.BottomLiftHeight, + CostDollars = PrintParametersSettings.CostDollars, + WeightG = PrintParametersSettings.WeightG + }, + SlicerInfoSettings = + { + AntiAliasLevel = SlicerInfoSettings.AntiAliasLevel, + MachineName = SlicerInfoSettings.MachineName, + //EncryptionMode = SlicerInfoSettings.EncryptionMode, + MachineNameSize = SlicerInfoSettings.MachineNameSize, + }, + Thumbnails = Thumbnails, + }; + + //file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + if (to == typeof(ChituboxZipFile)) { ChituboxZipFile file = new ChituboxZipFile @@ -1643,6 +1715,45 @@ namespace UVtools.Core 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(ResolutionX / HeaderSettings.BedSizeX, 3); + file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float)Math.Round(ResolutionY / HeaderSettings.BedSizeY, 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 = (uint)(PrintParametersSettings.LightOffDelay * 1000); + file.SliceSettings.LiftDistance = file.OutputSettings.LiftDistance = LiftHeight; + file.SliceSettings.LiftUpSpeed = file.OutputSettings.ZLiftFeedRate = LiftSpeed; + file.SliceSettings.LiftDownSpeed = file.OutputSettings.ZLiftRetractRate = RetractSpeed; + file.SliceSettings.LiftWhenFinished = defaultFormat.SliceSettings.LiftWhenFinished; + + file.OutputSettings.BlankingLayerTime = (uint) (PrintParametersSettings.LightOffDelay * 1000); + //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 = HeaderSettings.ProjectorType != 0; + file.OutputSettings.FlipY = file.OutputSettings.FlipX; + file.OutputSettings.AntiAliasingValue = ValidateAntiAliasingLevel(); + file.OutputSettings.AntiAliasing = file.OutputSettings.AntiAliasingValue > 1; + + file.Encode(fileFullPath, progress); + + return true; + } + return false; } #endregion diff --git a/UVtools.Core/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 5fea234..07749a7 100644 --- a/UVtools.Core/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -14,12 +14,14 @@ using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Util; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class ChituboxZipFile : FileFormat { @@ -242,6 +244,8 @@ namespace UVtools.Core progress.ItemCount = LayerCount; + var gcode = GCode.ToString(); + for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) { entry = inputFile.GetEntry($"{layerIndex+1}.png"); @@ -251,7 +255,28 @@ namespace UVtools.Core throw new FileLoadException($"Layer {layerIndex+1} not found", fileFullPath); } - LayerManager[layerIndex] = new Layer(layerIndex, entry.Open(), entry.Name); + var startStr = $";LAYER_START:{layerIndex}"; + var stripGcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length); + stripGcode = stripGcode.Substring(0, stripGcode.IndexOf(";LAYER_END")).Trim(' ', '\n', '\r', '\t'); + //var startCurrPos = stripGcode.Remove(0, ";currPos:".Length); + + var currPos = Regex.Match(stripGcode, ";currPos:([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase); + var exposureTime = Regex.Match(stripGcode, "G4 P(\\d+)", RegexOptions.IgnoreCase); + var pwm = Regex.Match(stripGcode, "M106 S(\\d+)", RegexOptions.IgnoreCase); + if (layerIndex < InitialLayerCount) + { + HeaderSettings.BottomLightPWM = byte.Parse(pwm.Groups[1].Value); + } + else + { + HeaderSettings.LayerLightPWM = byte.Parse(pwm.Groups[1].Value); + } + + LayerManager[layerIndex] = new Layer(layerIndex, entry.Open(), entry.Name) + { + PositionZ = float.Parse(currPos.Groups[1].Value), + ExposureTime = float.Parse(exposureTime.NextMatch().Groups[1].Value) / 1000f + }; progress++; } @@ -293,16 +318,26 @@ namespace UVtools.Core public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) { + void UpdateLayers() + { + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + this[layerIndex].ExposureTime = GetInitialLayerValueOrNormal(layerIndex, InitialExposureTime, LayerExposureTime); + } + } + if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) { HeaderSettings.BottomLayerCount = HeaderSettings.BottomLayCount = value.Convert<ushort>(); + UpdateLayers(); UpdateGCode(); return true; } if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) { HeaderSettings.BottomLayerExposureTime = value.Convert<float>(); + UpdateLayers(); UpdateGCode(); return true; } @@ -310,6 +345,7 @@ namespace UVtools.Core if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) { HeaderSettings.LayerExposureTime = value.Convert<float>(); + UpdateLayers(); UpdateGCode(); return true; } @@ -426,19 +462,47 @@ namespace UVtools.Core GCode.AppendLine(); GCode.AppendFormat(GCodeStart, Environment.NewLine); + float lastZPosition = 0; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { + var liftHeight = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLayerLiftHeight, + HeaderSettings.LayerLiftHeight); + + var liftZHeight = liftHeight + this[layerIndex].PositionZ; + + var liftZSpeed = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLayerLiftSpeed, + HeaderSettings.LayerLiftSpeed); + + var lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightOffTime, + HeaderSettings.LayerLightOffTime) * 1000; + + var pwmValue = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightPWM, HeaderSettings.LayerLightPWM); + var exposureTime = GetInitialLayerValueOrNormal(layerIndex, InitialExposureTime, LayerExposureTime) * 1000; + GCode.AppendLine($";LAYER_START:{layerIndex}"); - GCode.AppendLine($";currPos:{GetHeightFromLayer(layerIndex, false)}"); + GCode.AppendLine($";currPos:{this[layerIndex].PositionZ}"); 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"); + + // Absolute gcode + if (liftHeight > 0 && liftZHeight > this[layerIndex].PositionZ) + { + GCode.AppendLine($"G0 Z{liftZHeight} F{liftZSpeed};Z Lift"); + } + + if (lastZPosition < this[layerIndex].PositionZ) + { + GCode.AppendLine($"G0 Z{this[layerIndex].PositionZ} F{HeaderSettings.RetractSpeed};Layer position"); + } + + GCode.AppendLine($"G4 P{lightOffDelay};Before cure delay"); + GCode.AppendLine($"M106 S{pwmValue};light on"); + GCode.AppendLine($"G4 P{exposureTime};Cure time"); GCode.AppendLine("M106 S0;light off"); GCode.AppendLine(";LAYER_END"); GCode.AppendLine(); + + lastZPosition = this[layerIndex].PositionZ; } GCode.AppendFormat(GCodeEnd, Environment.NewLine, HeaderSettings.MachineZ); diff --git a/UVtools.Core/FileExtension.cs b/UVtools.Core/FileFormats/FileExtension.cs index 7838211..904da8c 100644 --- a/UVtools.Core/FileExtension.cs +++ b/UVtools.Core/FileFormats/FileExtension.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { /// <summary> /// Represents a file extension for slicer file formats diff --git a/UVtools.Core/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 018de42..89235cc 100644 --- a/UVtools.Core/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -12,13 +12,13 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using Emgu.CV; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { /// <summary> /// Slicer <see cref="FileFormat"/> representation @@ -274,7 +274,7 @@ namespace UVtools.Core public abstract float LayerHeight { get; } - public float TotalHeight => (float)Math.Round(LayerCount * LayerHeight, 2); + public float TotalHeight => LayerCount == 0 ? 0 : this[LayerCount - 1].PositionZ; //(float)Math.Round(LayerCount * LayerHeight, 2); public uint LayerCount => LayerManager?.Count ?? 0; diff --git a/UVtools.Core/IFileFormat.cs b/UVtools.Core/FileFormats/IFileFormat.cs index 058298d..8b88602 100644 --- a/UVtools.Core/IFileFormat.cs +++ b/UVtools.Core/FileFormats/IFileFormat.cs @@ -9,8 +9,9 @@ using System; using System.Text; using Emgu.CV; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { /// <summary> /// Slicer file format representation interface diff --git a/UVtools.Core/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index a9c6ac5..5e2761e 100644 --- a/UVtools.Core/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -1,11 +1,11 @@ using System; using System.IO; -using System.Threading.Tasks; using Emgu.CV; using Emgu.CV.CvEnum; +using UVtools.Core.Operations; using Size = System.Drawing.Size; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class ImageFile : FileFormat { diff --git a/UVtools.Core/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index bf1b791..c921493 100644 --- a/UVtools.Core/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -14,15 +14,15 @@ using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class PHZFile : FileFormat { @@ -453,9 +453,12 @@ namespace UVtools.Core 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); + LayerPositionZ = parent[layerIndex].PositionZ; + LayerExposure = parent[layerIndex].ExposureTime; + + LayerOffTimeSeconds = parent.GetInitialLayerValueOrNormal(layerIndex, + parent.HeaderSettings.BottomLightOffDelay, + parent.HeaderSettings.LayerOffTime); } public Mat Decode(uint layerIndex, bool consumeData = true) @@ -682,6 +685,7 @@ namespace UVtools.Core typeof(ChituboxZipFile), typeof(PWSFile), typeof(ZCodexFile), + typeof(CWSFile), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = @@ -819,7 +823,7 @@ namespace UVtools.Core Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => { if(progress.Token.IsCancellationRequested) return; - LayerData layer = new LayerData(this, (uint)layerIndex); + LayerData layer = new LayerData(this, (uint) layerIndex); using (var image = this[layerIndex].LayerMat) { layer.Encode(image, (uint) layerIndex); @@ -975,7 +979,11 @@ namespace UVtools.Core using (var image = LayersDefinitions[layerIndex].Decode((uint) layerIndex, true)) { - this[layerIndex] = new Layer((uint) layerIndex, image); + this[layerIndex] = new Layer((uint) layerIndex, image) + { + PositionZ = LayersDefinitions[layerIndex].LayerPositionZ, + ExposureTime = LayersDefinitions[layerIndex].LayerExposure + }; } lock (progress.Mutex) @@ -1015,8 +1023,10 @@ namespace UVtools.Core 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; + this[layerIndex].ExposureTime = + LayersDefinitions[layerIndex].LayerExposure = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomExposureSeconds, HeaderSettings.LayerExposureSeconds); + + LayersDefinitions[layerIndex].LayerOffTimeSeconds = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightOffDelay, HeaderSettings.LayerOffTime); } } @@ -1360,6 +1370,45 @@ namespace UVtools.Core 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(ResolutionX / HeaderSettings.BedSizeX, 3); + file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float)Math.Round(ResolutionY / HeaderSettings.BedSizeY, 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 = (uint)(HeaderSettings.LayerOffTime * 1000); + file.SliceSettings.LiftDistance = file.OutputSettings.LiftDistance = LiftHeight; + file.SliceSettings.LiftUpSpeed = file.OutputSettings.ZLiftFeedRate = LiftSpeed; + file.SliceSettings.LiftDownSpeed = file.OutputSettings.ZLiftRetractRate = RetractSpeed; + file.SliceSettings.LiftWhenFinished = defaultFormat.SliceSettings.LiftWhenFinished; + + file.OutputSettings.BlankingLayerTime = (uint)(HeaderSettings.LayerOffTime * 1000); + //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 = HeaderSettings.ProjectorType != 0; + file.OutputSettings.FlipY = file.OutputSettings.FlipX; + file.OutputSettings.AntiAliasingValue = ValidateAntiAliasingLevel(); + file.OutputSettings.AntiAliasing = file.OutputSettings.AntiAliasingValue > 1; + + file.Encode(fileFullPath, progress); + + return true; + } + return false; } #endregion diff --git a/UVtools.Core/PWSFile.cs b/UVtools.Core/FileFormats/PWSFile.cs index 45eb55b..c4c4fce 100644 --- a/UVtools.Core/PWSFile.cs +++ b/UVtools.Core/FileFormats/PWSFile.cs @@ -16,8 +16,9 @@ using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class PWSFile : FileFormat { @@ -403,8 +404,9 @@ namespace UVtools.Core Parent = parent; LiftHeight = Parent.HeaderSettings.LiftHeight; LiftSpeed = Parent.HeaderSettings.LiftSpeed; - LayerExposure = layerIndex < Parent.InitialLayerCount ? Parent.HeaderSettings.BottomExposureSeconds : Parent.HeaderSettings.LayerExposureTime; - LayerPositionZ = Parent.GetHeightFromLayer(layerIndex); + + LayerPositionZ = parent[layerIndex].PositionZ; + LayerExposure = parent[layerIndex].ExposureTime; } public Mat Decode(bool consumeData = true) @@ -754,7 +756,8 @@ namespace UVtools.Core { //typeof(ChituboxZipFile) //typeof(PHZFile), - //typeof(ZCodexFile), + typeof(PWSFile), + typeof(CWSFile), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = @@ -1007,7 +1010,11 @@ namespace UVtools.Core using (var image = LayersDefinition[(uint) layerIndex].Decode()) { - this[layerIndex] = new Layer((uint) layerIndex, image); + this[layerIndex] = new Layer((uint) layerIndex, image) + { + PositionZ = LayersDefinition[(uint)layerIndex].LayerPositionZ, + ExposureTime = LayersDefinition[(uint)layerIndex].LayerExposure + }; } lock (progress.Mutex) @@ -1035,9 +1042,9 @@ namespace UVtools.Core for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { // Bottom : others - LayersDefinition[layerIndex].LayerExposure = layerIndex < HeaderSettings.BottomLayersCount - ? HeaderSettings.BottomExposureSeconds - : HeaderSettings.LayerExposureTime; + + this[layerIndex].ExposureTime = + LayersDefinition[layerIndex].LayerExposure = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomExposureSeconds, HeaderSettings.LayerExposureTime); } } @@ -1129,6 +1136,39 @@ namespace UVtools.Core public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null) { + if (to == typeof(PWSFile)) + { + if (Path.GetExtension(FileFullPath).Equals(Path.GetExtension(fileFullPath))) + { + return false; + } + 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.Weight, + AntiAliasing = ValidateAntiAliasingLevel() + } + }; + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } /*if (to == typeof(PHZFile)) { PHZFile file = new PHZFile @@ -1262,6 +1302,46 @@ namespace UVtools.Core return true; } */ + + if (to == typeof(CWSFile)) + { + CWSFile defaultFormat = (CWSFile)FindByType(typeof(CWSFile)); + CWSFile file = new CWSFile { LayerManager = LayerManager }; + + //file.SliceSettings.Xppm = file.OutputSettings.PixPermmX = defaultFormat.OutputSettings. + //file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float)Math.Round(ResolutionY / HeaderSettings.BedSizeY, 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 = (uint)(HeaderSettings.LayerOffTime * 1000); + file.SliceSettings.LiftDistance = file.OutputSettings.LiftDistance = LiftHeight; + file.SliceSettings.LiftUpSpeed = file.OutputSettings.ZLiftFeedRate = LiftSpeed; + file.SliceSettings.LiftDownSpeed = file.OutputSettings.ZLiftRetractRate = RetractSpeed; + file.SliceSettings.LiftWhenFinished = defaultFormat.SliceSettings.LiftWhenFinished; + + file.OutputSettings.BlankingLayerTime = (uint)(HeaderSettings.LayerOffTime * 1000); + //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 = HeaderSettings. != 0; + //file.OutputSettings.FlipY = file.OutputSettings.FlipX; + file.OutputSettings.AntiAliasingValue = ValidateAntiAliasingLevel(); + file.OutputSettings.AntiAliasing = file.OutputSettings.AntiAliasingValue > 1; + + file.Encode(fileFullPath, progress); + + return true; + } + return false; } #endregion diff --git a/UVtools.Core/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index e92f193..3f88b2c 100644 --- a/UVtools.Core/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -13,12 +13,12 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; -using System.Threading.Tasks; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class SL1File : FileFormat { @@ -476,7 +476,11 @@ namespace UVtools.Core // - .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); + LayerManager[iLayer] = new Layer(iLayer, entity.Open(), entity.Name) + { + PositionZ = GetHeightFromLayer(iLayer), + ExposureTime = GetInitialLayerValueOrNormal(iLayer, InitialExposureTime, LayerExposureTime) + }; progress.ProcessedItems++; } } @@ -501,22 +505,37 @@ namespace UVtools.Core public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) { + void UpdateLayers() + { + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + this[layerIndex].ExposureTime = GetInitialLayerValueOrNormal(layerIndex, InitialExposureTime, LayerExposureTime); + } + } + if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) { PrintSettings.FadedLayers = OutputConfigSettings.NumFade = value.Convert<byte>(); + UpdateLayers(); return true; } if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) { MaterialSettings.InitialExposureTime = OutputConfigSettings.ExpTimeFirst = value.Convert<float>(); + + UpdateLayers(); + return true; } if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) { MaterialSettings.ExposureTime = OutputConfigSettings.ExpTime = value.Convert<float>(); + + UpdateLayers(); + return true; } @@ -890,13 +909,10 @@ namespace UVtools.Core if (to == typeof(CWSFile)) { CWSFile defaultFormat = (CWSFile)FindByType(typeof(CWSFile)); - CWSFile file = new CWSFile - { - LayerManager = LayerManager - }; + 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.Xppm = file.OutputSettings.PixPermmX = (float) Math.Round(LookupCustomValue<float>("Xppm", defaultFormat.SliceSettings.Xppm), 3); + file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float) Math.Round(LookupCustomValue<float>("Yppm", defaultFormat.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; diff --git a/UVtools.Core/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index cf7803e..c5a28bd 100644 --- a/UVtools.Core/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -17,8 +17,9 @@ using Emgu.CV.CvEnum; using Emgu.CV.Util; using Newtonsoft.Json; using UVtools.Core.Extensions; +using UVtools.Core.Operations; -namespace UVtools.Core +namespace UVtools.Core.FileFormats { public class ZCodexFile : FileFormat { @@ -175,9 +176,9 @@ namespace UVtools.Core public override ushort InitialLayerCount => ResinMetadataSettings.BottomLayersNumber; - public override float InitialExposureTime => UserSettings.BottomLayerExposureTime / 1000; + public override float InitialExposureTime => UserSettings.BottomLayerExposureTime / 1000f; - public override float LayerExposureTime => UserSettings.LayerExposureTime / 1000; + public override float LayerExposureTime => UserSettings.LayerExposureTime / 1000f; public override float LiftHeight => UserSettings.ZLiftDistance; public override float LiftSpeed => UserSettings.ZLiftFeedRate; @@ -227,13 +228,34 @@ namespace UVtools.Core GCode = new StringBuilder(GCodeStart); + float lastZPosition = 0; for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { progress.Token.ThrowIfCancellationRequested(); + Layer layer = this[layerIndex]; GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}"); - GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}"); - GCode.AppendLine($"G1 Z-{LiftHeight - LayerHeight} F{RetractSpeed}"); + + if (lastZPosition != layer.PositionZ) + { + if (LiftHeight > 0) + { + GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}"); + GCode.AppendLine($"G1 Z-{LiftHeight - layer.PositionZ + lastZPosition} F{RetractSpeed}"); + } + else + { + GCode.AppendLine($"G1 Z{layer.PositionZ- lastZPosition} F{LiftSpeed}"); + } + } + /*else + { + //GCode.AppendLine($";G1 Z{LiftHeight} F{LiftSpeed}; Already here"); + //GCode.AppendLine($";G1 Z-{LiftHeight - layer.PositionZ + lastZPosition} F{RetractSpeed}; Already here"); + }*/ + + //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); @@ -249,6 +271,8 @@ namespace UVtools.Core stream.Close(); } + lastZPosition = layer.PositionZ; + progress++; } @@ -308,6 +332,7 @@ namespace UVtools.Core int layerIndex = 0; int layerFileIndex = 0; string layerimagePath = null; + float currentHeight = 0; while (!ReferenceEquals(line = tr.ReadLine(), null)) { GCode.AppendLine(line); @@ -326,13 +351,59 @@ namespace UVtools.Core continue; } + /* + * +<Slice> 0 +G1 Z5.0 F100.0 +G1 Z-4.9 F100.0 +<Delay_blank> +M106 S255 +<Delay_support_full> +M106 S0 + */ + + var gcode = GCode.ToString(); + if (line.StartsWith(GCodeKeywordDelaySupportFull) || line.StartsWith(GCodeKeywordDelayModel)) { + var startStr = $"{GCodeKeywordSlice} {layerIndex}"; + var stripGcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length).Trim(' ', '\n', '\r', '\t'); + + var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase); + //var exposureTime = Regex.Match(stripGcode, ";<Delay> (\\d+)", RegexOptions.IgnoreCase); + /*var pwm = Regex.Match(stripGcode, "M106 S(\\d+)", RegexOptions.IgnoreCase); + if (layerIndex < InitialLayerCount) + { + OutputSettings.BottomLayerLightPWM = byte.Parse(pwm.Groups[1].Value); + } + else + { + OutputSettings.LayerLightPWM = byte.Parse(pwm.Groups[1].Value); + }*/ + if (currPos.Success) + { + var nextMatch = currPos.NextMatch(); + if (nextMatch.Success) + { + currentHeight = (float)Math.Round(currentHeight + float.Parse(currPos.Groups[1].Value) + float.Parse(currPos.NextMatch().Groups[1].Value), 2); + } + else + { + currentHeight = (float)Math.Round(currentHeight + float.Parse(currPos.Groups[1].Value), 2); + } + + } + LayersSettings[layerIndex].LayerFileIndex = layerFileIndex; LayersSettings[layerIndex].LayerEntry = inputFile.GetEntry(layerimagePath); - this[layerIndex] = new Layer((uint) layerIndex, LayersSettings[layerIndex].LayerEntry.Open(), LayersSettings[layerIndex].LayerEntry.Name); + this[layerIndex] = new Layer((uint) layerIndex, LayersSettings[layerIndex].LayerEntry.Open(), LayersSettings[layerIndex].LayerEntry.Name) + { + PositionZ = currentHeight, + ExposureTime = GetInitialLayerValueOrNormal((uint) layerIndex, InitialExposureTime, LayerExposureTime) + }; layerIndex++; - continue; + + progress++; } } @@ -356,22 +427,37 @@ namespace UVtools.Core public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) { + void UpdateLayers() + { + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + this[layerIndex].ExposureTime = GetInitialLayerValueOrNormal(layerIndex, InitialExposureTime, LayerExposureTime); + } + } + if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount)) { UserSettings.BottomLayersCount = ResinMetadataSettings.BottomLayersNumber = value.Convert<ushort>(); + UpdateLayers(); return true; } if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds)) { ResinMetadataSettings.BottomLayersTime = UserSettings.BottomLayerExposureTime = value.Convert<uint>()*1000; + + UpdateLayers(); + return true; } if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds)) { ResinMetadataSettings.LayerTime = UserSettings.LayerExposureTime = value.Convert<uint>()*1000; + + UpdateLayers(); + return true; } diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs new file mode 100644 index 0000000..1842d73 --- /dev/null +++ b/UVtools.Core/Layer/Layer.cs @@ -0,0 +1,692 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using Emgu.CV.Util; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core +{ + /// <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 number of non zero pixels on this layer image + /// </summary> + public uint NonZeroPixelCount { get; private protected set; } + + /// <summary> + /// Gets the bounding rectangle for the image area + /// </summary> + public Rectangle BoundingRectangle { get; private protected set; } = Rectangle.Empty; + + /// <summary> + /// Gets the layer index + /// </summary> + public uint Index { get; } + + /// <summary> + /// Gets or sets the exposure time in seconds + /// </summary> + public float ExposureTime { get; set; } + + /// <summary> + /// Gets or sets the layer position on Z in mm + /// </summary> + public float PositionZ { get; set; } + + private byte[] _compressedBytes; + /// <summary> + /// Gets or sets layer image compressed data + /// </summary> + public byte[] CompressedBytes + { + get => LayerManager.DecompressLayer(_compressedBytes); + set + { + _compressedBytes = LayerManager.CompressLayer(value); + IsModified = true; + if (!ReferenceEquals(ParentLayerManager, null)) + ParentLayerManager.BoundingRectangle = Rectangle.Empty; + } + } + + /// <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 Mat LayerMat + { + get + { + Mat mat = new Mat(); + CvInvoke.Imdecode(CompressedBytes, ImreadModes.Grayscale, mat); + return mat; + } + set + { + using (var vector = new VectorOfByte()) + { + CvInvoke.Imencode(".png", value, vector); + CompressedBytes = vector.ToArray(); + + GetBoundingRectangle(value, true); + } + } + } + + /// <summary> + /// Gets a new Brg image instance + /// </summary> + public Mat BrgMat + { + get + { + Mat mat = LayerMat; + CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr); + return mat; + } + } + + #endregion + + #region Constructor + public Layer(uint index, byte[] compressedBytes, string filename = null, LayerManager pararentLayerManager = null) + { + ParentLayerManager = pararentLayerManager; + Index = index; + Filename = filename ?? $"Layer{index}.png"; + CompressedBytes = compressedBytes; + IsModified = false; + /*if (compressedBytes.Length > 0) + { + GetBoundingRectangle(); + }*/ + } + + public Layer(uint index, Mat layerMat, string filename = null, LayerManager pararentLayerManager = null) : this(index, new byte[0], filename, pararentLayerManager) + { + LayerMat = layerMat; + 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(_compressedBytes, other._compressedBytes); + } + + 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 (_compressedBytes != null ? _compressedBytes.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 Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false) + { + if (NonZeroPixelCount > 0 && !reCalculate) + { + return BoundingRectangle; + } + bool needDispose = false; + if (ReferenceEquals(mat, null)) + { + mat = LayerMat; + needDispose = true; + } + + using (var nonZeroMat = new Mat()) + { + CvInvoke.FindNonZero(mat, nonZeroMat); + NonZeroPixelCount = (uint)nonZeroMat.Rows / 2; + BoundingRectangle = CvInvoke.BoundingRectangle(nonZeroMat); + } + + + if (needDispose) mat.Dispose(); + + return BoundingRectangle; + } + + 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 mat = LayerMat; + var bytes = mat.GetPixelSpan<byte>(); + + + + var previousLayerImage = PreviousLayer()?.LayerMat; + var previousBytes = previousLayerImage?.GetBytes(); + + + /*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[mat.Width, mat.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 < mat.Height; y++) + { + for (int x = 0; x < mat.Width; x++) + { + pixelIndex = y * mat.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 * mat.Width + tempx2; + if (tempy2 >= 0 && + tempy2 < mat.Height && + tempx2 >= 0 && tempx2 < mat.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 < mat.Width; x++) // Check Top and Bottom bounds + { + if (bytes[x] >= 200) // Top + { + pixels.Add(new Point(x, 0)); + } + + if (bytes[mat.Width * mat.Height - mat.Width + x] >= 200) // Bottom + { + pixels.Add(new Point(x, mat.Height - 1)); + } + } + + for (int y = 0; y < mat.Height; y++) // Check Left and Right bounds + { + if (bytes[y * mat.Width] >= 200) // Left + { + pixels.Add(new Point(0, y)); + } + + if (bytes[y * mat.Width + mat.Width - 1] >= 200) // Right + { + pixels.Add(new Point(mat.Width - 1, y)); + } + } + + if (pixels.Count > 0) + { + result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray())); + } + + pixels.Clear(); + + return result; + } + + public void MutateMove(OperationMove move) + { + using (var layer = LayerMat) + { + if (move.ImageWidth == 0) move.ImageWidth = (uint)layer.Width; + if (move.ImageHeight == 0) move.ImageHeight = (uint)layer.Height; + + /*layer.Transform(1.0, 1.0, move.MarginLeft - move.MarginRight, move.MarginTop-move.MarginBottom); + LayerMat = layer;*/ + using (var layerRoi = new Mat(layer, move.SrcRoi)) + { + using (var dstLayer = layer.CloneBlank()) + { + using (var dstRoi = new Mat(dstLayer, move.DstRoi)) + { + layerRoi.CopyTo(dstRoi); + LayerMat = dstLayer; + } + } + } + } + } + + + public void MutateResize(double xScale, double yScale) + { + using (var mat = LayerMat) + { + mat.TransformFromCenter(xScale, yScale); + LayerMat = mat; + } + } + + public void MutateFlip(FlipType flipType, bool makeCopy = true) + { + using (var mat = LayerMat) + { + if (makeCopy) + { + using (Mat dst = new Mat()) + { + CvInvoke.Flip(mat, dst, flipType); + var spanSrc = mat.GetPixelSpan<byte>(); + var spanDst = dst.GetPixelSpan<byte>(); + for (int i = 0; i < spanSrc.Length; i++) + { + if (spanDst[i] == 0) continue; + spanSrc[i] = spanDst[i]; + } + + LayerMat = mat; + + } + } + else + { + CvInvoke.Flip(mat, mat, flipType); + } + + LayerMat = mat; + } + } + + public void MutateRotate(double angle = 90.0, Inter interpolation = Inter.Linear) + { + using (var mat = LayerMat) + { + var halfWidth = mat.Width / 2.0f; + var halfHeight = mat.Height / 2.0f; + using (var translateTransform = new Matrix<double>(2, 3)) + { + CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), angle, 1.0, translateTransform); + /*var rect = new RotatedRect(PointF.Empty, mat.Size, (float) angle).MinAreaRect(); + translateTransform[0, 2] += rect.Width / 2.0 - mat.Cols / 2.0; + translateTransform[0, 2] += rect.Height / 2.0 - mat.Rows / 2.0;*/ + + /* var abs_cos = Math.Abs(translateTransform[0, 0]); + var abs_sin = Math.Abs(translateTransform[0, 1]); + + var bound_w = mat.Height * abs_sin + mat.Width * abs_cos; + var bound_h = mat.Height * abs_cos + mat.Width * abs_sin; + + translateTransform[0, 2] += bound_w / 2 - halfWidth; + translateTransform[1, 2] += bound_h / 2 - halfHeight;*/ + + + CvInvoke.WarpAffine(mat, mat, translateTransform, mat.Size, interpolation); + } + + 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 void ToolPattern(OperationPattern settings) + { + using (var layer = LayerMat) + { + using (var layerRoi = new Mat(layer, settings.SrcRoi)) + { + using (var dstLayer = layer.CloneBlank()) + { + for (ushort col = 0; col < settings.Cols; col++) + { + for (ushort row = 0; row < settings.Rows; row++) + { + using (var dstRoi = new Mat(dstLayer, settings.GetRoi(col, row))) + { + layerRoi.CopyTo(dstRoi); + } + } + } + + LayerMat = dstLayer; + } + } + } + } + + + + public Layer Clone() + { + return new Layer(Index, CompressedBytes, Filename, ParentLayerManager); + } + + #endregion + } +} diff --git a/UVtools.Core/Layer/LayerIssue.cs b/UVtools.Core/Layer/LayerIssue.cs new file mode 100644 index 0000000..8618d89 --- /dev/null +++ b/UVtools.Core/Layer/LayerIssue.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Text; + +namespace UVtools.Core +{ + #region LayerIssue Class + + public class IslandDetectionConfiguration + { + /// <summary> + /// Gets if the detection is enabled + /// </summary> + public bool Enabled { get; set; } = true; + + /// <summary> + /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white + /// Set to 0 to disable this operation + /// </summary> + public byte BinaryThreshold { get; set; } = 1; + + /// <summary> + /// Gets the required area size (x*y) to consider process a island (0-255) + /// </summary> + public byte RequiredAreaToProcessCheck { get; set; } = 1; + + /// <summary> + /// Gets the required brightness for check a pixel under a island (0-255) + /// </summary> + public byte RequiredPixelBrightnessToProcessCheck { get; set; } = 10; + + /// <summary> + /// Gets the required number of pixels to support a island and discard it as a issue (0-255) + /// </summary> + public byte RequiredPixelsToSupport { get; set; } = 10; + + /// <summary> + /// Gets the required brightness of supporting pixels to count as a valid support (0-255) + /// </summary> + public byte RequiredPixelBrightnessToSupport { get; set; } = 150; + } + + public class ResinTrapDetectionConfiguration + { + /// <summary> + /// Gets if the detection is enabled + /// </summary> + public bool Enabled { get; set; } = true; + + /// <summary> + /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white + /// Set to 0 to disable this operation + /// </summary> + public byte BinaryThreshold { get; set; } = 127; + + /// <summary> + /// Gets the required area size (x*y) to consider process a hollow area (0-255) + /// </summary> + public byte RequiredAreaToProcessCheck { get; set; } = 1; + + /// <summary> + /// Gets the number of black pixels required to consider a drain + /// </summary> + public byte RequiredBlackPixelsToDrain { get; set; } = 10; + + /// <summary> + /// Gets the maximum pixel brightness to be a drain pixel (0-150) + /// </summary> + public byte MaximumPixelBrightnessToDrain { get; set; } = 30; + } + + + 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}, Layer: {Layer.Index}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Size)}: {Size}"; + } + } + #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 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, Rectangle boundingRectangle, AreaType type = AreaType.Unknown) + { + Contour = contour; + BoundingRectangle = boundingRectangle; + Type = type; + } + } + #endregion +} diff --git a/UVtools.Core/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 86abf92..cf21310 100644 --- a/UVtools.Core/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -19,929 +19,10 @@ using Emgu.CV.CvEnum; using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core.Extensions; +using UVtools.Core.Operations; namespace UVtools.Core { - - #region LayerIssue Class - - public class IslandDetectionConfiguration - { - /// <summary> - /// Gets if the detection is enabled - /// </summary> - public bool Enabled { get; set; } = true; - - /// <summary> - /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white - /// Set to 0 to disable this operation - /// </summary> - public byte BinaryThreshold { get; set; } = 1; - - /// <summary> - /// Gets the required area size (x*y) to consider process a island (0-255) - /// </summary> - public byte RequiredAreaToProcessCheck { get; set; } = 1; - - /// <summary> - /// Gets the required brightness for check a pixel under a island (0-255) - /// </summary> - public byte RequiredPixelBrightnessToProcessCheck { get; set; } = 10; - - /// <summary> - /// Gets the required number of pixels to support a island and discard it as a issue (0-255) - /// </summary> - public byte RequiredPixelsToSupport { get; set; } = 10; - - /// <summary> - /// Gets the required brightness of supporting pixels to count as a valid support (0-255) - /// </summary> - public byte RequiredPixelBrightnessToSupport { get; set; } = 150; - } - - public class ResinTrapDetectionConfiguration - { - /// <summary> - /// Gets if the detection is enabled - /// </summary> - public bool Enabled { get; set; } = true; - - /// <summary> - /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white - /// Set to 0 to disable this operation - /// </summary> - public byte BinaryThreshold { get; set; } = 127; - - /// <summary> - /// Gets the required area size (x*y) to consider process a hollow area (0-255) - /// </summary> - public byte RequiredAreaToProcessCheck { get; set; } = 1; - - /// <summary> - /// Gets the number of black pixels required to consider a drain - /// </summary> - public byte RequiredBlackPixelsToDrain { get; set; } = 10; - - /// <summary> - /// Gets the maximum pixel brightness to be a drain pixel (0-150) - /// </summary> - public byte MaximumPixelBrightnessToDrain { get; set; } = 30; - } - - - 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}, Layer: {Layer.Index}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Size)}: {Size}"; - } - } - #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 number of non zero pixels on this layer image - /// </summary> - public uint NonZeroPixelCount { get; private protected set; } - - /// <summary> - /// Gets the bounding rectangle for the image area - /// </summary> - public Rectangle BoundingRectangle { get; private protected set; } = Rectangle.Empty; - - /// <summary> - /// Gets the layer index - /// </summary> - public uint Index { get; } - - /// <summary> - /// Gets or sets the exposure time in raw value - /// </summary> - public float ExposureTime { get; set; } - - /// <summary> - /// Gets or sets the layer position on Z in raw value - /// </summary> - public float PositionZ { get; set; } - - private byte[] _compressedBytes; - /// <summary> - /// Gets or sets layer image compressed data - /// </summary> - public byte[] CompressedBytes - { - get => LayerManager.DecompressLayer(_compressedBytes); - set - { - _compressedBytes = LayerManager.CompressLayer(value); - IsModified = true; - if(!ReferenceEquals(ParentLayerManager, null)) - ParentLayerManager.BoundingRectangle = Rectangle.Empty; - } - } - - /// <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 Mat LayerMat - { - get - { - Mat mat = new Mat(); - CvInvoke.Imdecode(CompressedBytes, ImreadModes.Grayscale, mat); - return mat; - } - set - { - using (var vector = new VectorOfByte()) - { - CvInvoke.Imencode(".png", value, vector); - CompressedBytes = vector.ToArray(); - - GetBoundingRectangle(value, true); - } - } - } - - /// <summary> - /// Gets a new Brg image instance - /// </summary> - public Mat BrgMat - { - get - { - Mat mat = LayerMat; - CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr); - return mat; - } - } - - #endregion - - #region Constructor - public Layer(uint index, byte[] compressedBytes, string filename = null, LayerManager pararentLayerManager = null) - { - ParentLayerManager = pararentLayerManager; - Index = index; - Filename = filename ?? $"Layer{index}.png"; - CompressedBytes = compressedBytes; - IsModified = false; - /*if (compressedBytes.Length > 0) - { - GetBoundingRectangle(); - }*/ - } - - public Layer(uint index, Mat layerMat, string filename = null, LayerManager pararentLayerManager = null) : this(index, new byte[0], filename, pararentLayerManager) - { - LayerMat = layerMat; - 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(_compressedBytes, other._compressedBytes); - } - - 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 (_compressedBytes != null ? _compressedBytes.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 Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false) - { - if (NonZeroPixelCount > 0 && !reCalculate) - { - return BoundingRectangle; - } - bool needDispose = false; - if (ReferenceEquals(mat, null)) - { - mat = LayerMat; - needDispose = true; - } - - using (var nonZeroMat = new Mat()) - { - CvInvoke.FindNonZero(mat, nonZeroMat); - NonZeroPixelCount = (uint)nonZeroMat.Rows / 2; - BoundingRectangle = CvInvoke.BoundingRectangle(nonZeroMat); - } - - - if(needDispose) mat.Dispose(); - - return BoundingRectangle; - } - - 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 mat = LayerMat; - var bytes = mat.GetPixelSpan<byte>(); - - - - var previousLayerImage = PreviousLayer()?.LayerMat; - var previousBytes = previousLayerImage?.GetBytes(); - - - /*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[mat.Width, mat.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 < mat.Height; y++) - { - for (int x = 0; x < mat.Width; x++) - { - pixelIndex = y * mat.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 * mat.Width + tempx2; - if (tempy2 >= 0 && - tempy2 < mat.Height && - tempx2 >= 0 && tempx2 < mat.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 < mat.Width; x++) // Check Top and Bottom bounds - { - if (bytes[x] >= 200) // Top - { - pixels.Add(new Point(x, 0)); - } - - if (bytes[mat.Width * mat.Height - mat.Width + x] >= 200) // Bottom - { - pixels.Add(new Point(x, mat.Height-1)); - } - } - - for (int y = 0; y < mat.Height; y++) // Check Left and Right bounds - { - if (bytes[y * mat.Width] >= 200) // Left - { - pixels.Add(new Point(0, y)); - } - - if (bytes[y * mat.Width + mat.Width - 1] >= 200) // Right - { - pixels.Add(new Point(mat.Width-1, y)); - } - } - - if (pixels.Count > 0) - { - result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray())); - } - - pixels.Clear(); - - return result; - } - - public void MutateMove(OperationMove move) - { - using (var layer = LayerMat) - { - if (move.ImageWidth == 0) move.ImageWidth = (uint) layer.Width; - if (move.ImageHeight == 0) move.ImageHeight = (uint) layer.Height; - - /*layer.Transform(1.0, 1.0, move.MarginLeft - move.MarginRight, move.MarginTop-move.MarginBottom); - LayerMat = layer;*/ - using (var layerRoi = new Mat(layer, move.SrcRoi)) - { - using (var dstLayer = layer.CloneBlank()) - { - using (var dstRoi = new Mat(dstLayer, move.DstRoi)) - { - layerRoi.CopyTo(dstRoi); - LayerMat = dstLayer; - } - } - } - } - } - - - public void MutateResize(double xScale, double yScale) - { - using (var mat = LayerMat) - { - mat.TransformFromCenter(xScale, yScale); - LayerMat = mat; - } - } - - public void MutateFlip(FlipType flipType, bool makeCopy = true) - { - using (var mat = LayerMat) - { - if (makeCopy) - { - using (Mat dst = new Mat()) - { - CvInvoke.Flip(mat, dst, flipType); - var spanSrc = mat.GetPixelSpan<byte>(); - var spanDst = dst.GetPixelSpan<byte>(); - for (int i = 0; i < spanSrc.Length; i++) - { - if (spanDst[i] == 0) continue; - spanSrc[i] = spanDst[i]; - } - - LayerMat = mat; - - } - } - else - { - CvInvoke.Flip(mat, mat, flipType); - } - - LayerMat = mat; - } - } - - public void MutateRotate(double angle = 90.0, Inter interpolation = Inter.Linear) - { - using (var mat = LayerMat) - { - var halfWidth = mat.Width / 2.0f; - var halfHeight = mat.Height / 2.0f; - using (var translateTransform = new Matrix<double>(2, 3)) - { - CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), angle, 1.0, translateTransform); - /*var rect = new RotatedRect(PointF.Empty, mat.Size, (float) angle).MinAreaRect(); - translateTransform[0, 2] += rect.Width / 2.0 - mat.Cols / 2.0; - translateTransform[0, 2] += rect.Height / 2.0 - mat.Rows / 2.0;*/ - - /* var abs_cos = Math.Abs(translateTransform[0, 0]); - var abs_sin = Math.Abs(translateTransform[0, 1]); - - var bound_w = mat.Height * abs_sin + mat.Width * abs_cos; - var bound_h = mat.Height * abs_cos + mat.Width * abs_sin; - - translateTransform[0, 2] += bound_w / 2 - halfWidth; - translateTransform[1, 2] += bound_h / 2 - halfHeight;*/ - - - CvInvoke.WarpAffine(mat, mat, translateTransform, mat.Size, interpolation); - } - - 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 void ToolPattern(OperationPattern settings) - { - using (var layer = LayerMat) - { - using (var layerRoi = new Mat(layer, settings.SrcRoi)) - { - using (var dstLayer = layer.CloneBlank()) - { - for (ushort col = 0; col < settings.Cols; col++) - { - for (ushort row = 0; row < settings.Rows; row++) - { - using (var dstRoi = new Mat(dstLayer, settings.GetRoi(col, row))) - { - layerRoi.CopyTo(dstRoi); - } - } - } - - LayerMat = dstLayer; - } - } - } - } - - - - public Layer Clone() - { - return new Layer(Index, CompressedBytes, Filename, ParentLayerManager); - } - - - - #endregion - } - #endregion - - #region LayerManager Class public class LayerManager : IEnumerable<Layer> { #region Enums @@ -2077,5 +1158,4 @@ namespace UVtools.Core #endregion } - #endregion } diff --git a/UVtools.Core/MatBytes.cs b/UVtools.Core/MatBytes.cs deleted file mode 100644 index 1dbb46a..0000000 --- a/UVtools.Core/MatBytes.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Drawing; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Emgu.CV; -using Emgu.CV.CvEnum; - -namespace UVtools.Core -{ - public sealed class MatBytes : IDisposable - { - private bool m_IsDisposed; - /// <summary> - /// Gets the byte structure of this Mat - /// </summary> - public byte[] Bytes; - - /// <summary> - /// Gets the <see cref="Mat"/> - /// </summary> - public Mat Mat { get; } - - /// <summary> - /// Gets the <see cref="GCHandle"/> for the allocated bytes - /// </summary> - public GCHandle Handle { get; } - - public byte this[int index] - { - get => Bytes[index]; - set => Bytes[index] = value; - } - - public byte this[int x, int y] - { - get => Bytes[y * Mat.Width + x]; - set => Bytes[y * Mat.Width + x] = value; - } - - public MatBytes(Mat mat) : this(mat.Size, (byte) mat.NumberOfChannels, mat.Depth) - { - var s = mat.DataPointer; - - } - - public MatBytes(Size size, byte channels = 1, DepthType depth = DepthType.Cv8U) - { - Bytes = new byte[size.Width * size.Height * channels]; - Handle = GCHandle.Alloc(Bytes, GCHandleType.Pinned); - Mat = new Mat(size, depth, channels, Handle.AddrOfPinnedObject(), size.Width * channels); - } - - public MatBytes(Size size, out byte[] bytes, byte channels = 1, DepthType depth = DepthType.Cv8U) : this(size, channels, depth) - { - bytes = Bytes; - } - - public void Free() - { - Handle.Free(); - } - - [MethodImpl(MethodImplOptions.Synchronized)] - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool isDisposing) - { - if (m_IsDisposed) - return; - - if (isDisposing) - { - Mat?.Dispose(); - Free(); - } - m_IsDisposed = true; - } - - } -} diff --git a/UVtools.Core/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs index 8707be2..bed0879 100644 --- a/UVtools.Core/OperationMove.cs +++ b/UVtools.Core/Operations/OperationMove.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Text; -namespace UVtools.Core +namespace UVtools.Core.Operations { public enum Anchor : byte { diff --git a/UVtools.Core/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs index 0f4f668..9332a7e 100644 --- a/UVtools.Core/OperationPattern.cs +++ b/UVtools.Core/Operations/OperationPattern.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace UVtools.Core +namespace UVtools.Core.Operations { public class OperationPattern { diff --git a/UVtools.Core/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 8b6559d..7c6ed73 100644 --- a/UVtools.Core/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -1,7 +1,7 @@ using System; using System.Threading; -namespace UVtools.Core +namespace UVtools.Core.Operations { public sealed class OperationProgress { diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 153a0b2..7fddd8e 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> - <Version>0.6.1.0</Version> + <Version>0.6.1.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> </PropertyGroup> @@ -24,10 +24,6 @@ </PropertyGroup> <ItemGroup> - <Compile Remove="MatBytes.cs" /> - </ItemGroup> - - <ItemGroup> <None Include="..\LICENSE"> <Pack>True</Pack> <PackagePath></PackagePath> @@ -36,7 +32,6 @@ <Pack>True</Pack> <PackagePath></PackagePath> </None> - <None Include="MatBytes.cs" /> </ItemGroup> <ItemGroup> diff --git a/UVtools.GUI/Forms/FrmAbout.cs b/UVtools.GUI/Forms/FrmAbout.cs index baeebdc..846dba8 100644 --- a/UVtools.GUI/Forms/FrmAbout.cs +++ b/UVtools.GUI/Forms/FrmAbout.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.FileFormats; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/Forms/FrmInputBox.cs b/UVtools.GUI/Forms/FrmInputBox.cs index 7d014dc..83328ed 100644 --- a/UVtools.GUI/Forms/FrmInputBox.cs +++ b/UVtools.GUI/Forms/FrmInputBox.cs @@ -10,6 +10,7 @@ using System; using System.Globalization; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.FileFormats; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/Forms/FrmLoading.cs b/UVtools.GUI/Forms/FrmLoading.cs index 9774a9f..10ebd38 100644 --- a/UVtools.GUI/Forms/FrmLoading.cs +++ b/UVtools.GUI/Forms/FrmLoading.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Threading.Tasks; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.Operations; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/Forms/FrmMutationMove.cs b/UVtools.GUI/Forms/FrmMutationMove.cs index 42ebbc0..4ac6a04 100644 --- a/UVtools.GUI/Forms/FrmMutationMove.cs +++ b/UVtools.GUI/Forms/FrmMutationMove.cs @@ -10,6 +10,7 @@ using System; using System.Drawing; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.Operations; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/Forms/FrmSettings.cs b/UVtools.GUI/Forms/FrmSettings.cs index e2b4d1b..b385aba 100644 --- a/UVtools.GUI/Forms/FrmSettings.cs +++ b/UVtools.GUI/Forms/FrmSettings.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.FileFormats; using UVtools.GUI.Properties; namespace UVtools.GUI.Forms diff --git a/UVtools.GUI/Forms/FrmToolPattern.cs b/UVtools.GUI/Forms/FrmToolPattern.cs index 49e690d..34cc9c1 100644 --- a/UVtools.GUI/Forms/FrmToolPattern.cs +++ b/UVtools.GUI/Forms/FrmToolPattern.cs @@ -10,6 +10,7 @@ using System; using System.Drawing; using System.Windows.Forms; using UVtools.Core; +using UVtools.Core.Operations; namespace UVtools.GUI.Forms { diff --git a/UVtools.GUI/FrmMain.Designer.cs b/UVtools.GUI/FrmMain.Designer.cs index 75ad320..b28e316 100644 --- a/UVtools.GUI/FrmMain.Designer.cs +++ b/UVtools.GUI/FrmMain.Designer.cs @@ -138,7 +138,7 @@ this.btnNextLayer = new System.Windows.Forms.Button(); this.lbMaxLayer = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); - this.lbLayerActual = new System.Windows.Forms.Label(); + this.lbActualLayer = new System.Windows.Forms.Label(); this.tbLayer = new System.Windows.Forms.TrackBar(); this.lbInitialLayer = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); @@ -1266,7 +1266,7 @@ // // panel1 // - this.panel1.Controls.Add(this.lbLayerActual); + this.panel1.Controls.Add(this.lbActualLayer); this.panel1.Controls.Add(this.tbLayer); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(3, 85); @@ -1276,13 +1276,13 @@ // // lbLayerActual // - this.lbLayerActual.AutoSize = true; - this.lbLayerActual.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; - this.lbLayerActual.Location = new System.Drawing.Point(3, 248); - this.lbLayerActual.Name = "lbLayerActual"; - this.lbLayerActual.Size = new System.Drawing.Size(15, 15); - this.lbLayerActual.TabIndex = 9; - this.lbLayerActual.Text = "?"; + this.lbActualLayer.AutoSize = true; + this.lbActualLayer.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.lbActualLayer.Location = new System.Drawing.Point(3, 248); + this.lbActualLayer.Name = "lbActualLayer"; + this.lbActualLayer.Size = new System.Drawing.Size(15, 15); + this.lbActualLayer.TabIndex = 9; + this.lbActualLayer.Text = "?"; // // tbLayer // @@ -1520,7 +1520,7 @@ private System.Windows.Forms.Button btnNextLayer; private System.Windows.Forms.Button btnPreviousLayer; private System.Windows.Forms.Panel panel1; - private System.Windows.Forms.Label lbLayerActual; + private System.Windows.Forms.Label lbActualLayer; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Button btnFirstLayer; private System.Windows.Forms.Button btnLastLayer; diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs index b40adbf..c2e9dd9 100644 --- a/UVtools.GUI/FrmMain.cs +++ b/UVtools.GUI/FrmMain.cs @@ -22,6 +22,8 @@ using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core; using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; +using UVtools.Core.Operations; using UVtools.GUI.Forms; using UVtools.GUI.Properties; @@ -332,10 +334,10 @@ namespace UVtools.GUI ZoomToFit(); } - lbLayerActual.Location = new Point(lbLayerActual.Location.X, + lbActualLayer.Location = new Point(lbActualLayer.Location.X, Math.Max(1, Math.Min(tbLayer.Height - 40, - (int)(tbLayer.Height - tbLayer.Value * ((float)tbLayer.Height / tbLayer.Maximum)) - lbLayerActual.Height / 2) + (int)(tbLayer.Height - tbLayer.Value * ((float)tbLayer.Height / tbLayer.Maximum)) - lbActualLayer.Height / 2) )); } @@ -1294,11 +1296,11 @@ namespace UVtools.GUI DisableGUI(); FrmLoading.SetDescription($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(dialog.FileName)}"); - Task task = Task.Factory.StartNew(() => + Task<bool> task = Task<bool>.Factory.StartNew(() => { try { - SlicerFile.Convert(fileFormat, dialog.FileName, FrmLoading.RestartProgress()); + return SlicerFile.Convert(fileFormat, dialog.FileName, FrmLoading.RestartProgress()); } catch (OperationCanceledException) { @@ -1315,9 +1317,11 @@ namespace UVtools.GUI EnableGUI(true); }); } + + return false; }); - if (FrmLoading.ShowDialog() == DialogResult.OK) + if (FrmLoading.ShowDialog() == DialogResult.OK && task.Result) { if (MessageBox.Show($"Convertion is completed: {Path.GetFileName(dialog.FileName)} in {FrmLoading.StopWatch.ElapsedMilliseconds / 1000}s\n" + "Do you want open the converted file in a new window?", @@ -1486,7 +1490,7 @@ namespace UVtools.GUI UpdateIssuesInfo(); lbMaxLayer.Text = - lbLayerActual.Text = + lbActualLayer.Text = lbInitialLayer.Text = "???"; lvProperties.BeginUpdate(); lvProperties.Items.Clear(); @@ -1902,6 +1906,8 @@ namespace UVtools.GUI btnLastLayer.Enabled = btnNextLayer.Enabled = layerNum < SlicerFile.LayerCount - 1; btnFirstLayer.Enabled = btnPreviousLayer.Enabled = layerNum > 0; + var layer = SlicerFile[ActualLayer]; + try { // OLD @@ -2123,17 +2129,17 @@ namespace UVtools.GUI watch.Stop(); tsLayerPreviewTime.Text = $"{watch.ElapsedMilliseconds}ms"; //lbLayers.Text = $"{SlicerFile.GetHeightFromLayer(layerNum)} / {SlicerFile.TotalHeight}mm\n{layerNum} / {SlicerFile.LayerCount-1}\n{percent}%"; - lbLayerActual.Text = $"{SlicerFile.GetHeightFromLayer(ActualLayer)}mm\n{ActualLayer}\n{percent}%"; - lbLayerActual.Location = new Point(lbLayerActual.Location.X, + lbActualLayer.Text = $"{layer.PositionZ}mm\n{ActualLayer}\n{percent}%"; + lbActualLayer.Location = new Point(lbActualLayer.Location.X, Math.Max(1, - Math.Min(tbLayer.Height- lbLayerActual.Height, - (int)(tbLayer.Height - tbLayer.Value * ((float)tbLayer.Height / tbLayer.Maximum)) - lbLayerActual.Height/2) + Math.Min(tbLayer.Height- lbActualLayer.Height, + (int)(tbLayer.Height - tbLayer.Value * ((float)tbLayer.Height / tbLayer.Maximum)) - lbActualLayer.Height/2) )); pbLayers.Value = percent; - lbLayerActual.Invalidate(); - lbLayerActual.Update(); - lbLayerActual.Refresh(); + lbActualLayer.Invalidate(); + lbActualLayer.Update(); + lbActualLayer.Refresh(); pbLayer.Invalidate(); pbLayer.Update(); pbLayer.Refresh(); diff --git a/UVtools.GUI/Program.cs b/UVtools.GUI/Program.cs index 675c1fa..45ebbf6 100644 --- a/UVtools.GUI/Program.cs +++ b/UVtools.GUI/Program.cs @@ -14,6 +14,7 @@ using System.Windows.Forms; using ApplicationManagement; using Emgu.CV; using UVtools.Core; +using UVtools.Core.FileFormats; using UVtools.GUI.Forms; namespace UVtools.GUI diff --git a/UVtools.GUI/Properties/AssemblyInfo.cs b/UVtools.GUI/Properties/AssemblyInfo.cs index 2fea0eb..1998be3 100644 --- a/UVtools.GUI/Properties/AssemblyInfo.cs +++ b/UVtools.GUI/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.6.1.0")] -[assembly: AssemblyFileVersion("0.6.1.0")] +[assembly: AssemblyVersion("0.6.1.1")] +[assembly: AssemblyFileVersion("0.6.1.1")] |