diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-17 05:34:31 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-17 05:34:31 +0300 |
commit | 51c82318cf27b8e0d3425aa2ea1b093fc5b7d23f (patch) | |
tree | 1059ece01344eec934792d72bf8b0ed9aad4a06d /UVtools.Core | |
parent | edd9984a31a90791edf3bcf594855d08507428b5 (diff) |
v2.12.0v2.12.0
- **Layer arithmetic:**
- (Add) Allow to use ':' to define a layer range to set, eg, 0:20 to select from 0 to 20 layers
- (Improvement) Modifications with set ROI and/or Mask(s) are only applied to target layer on that same regions
- (Improvement) Disallow set one layer to the same layer without any modification
- (Improvement) Clear and sanitize non-existing layers indexes
- (Improvement) Disable the layer range selector from dialog
- (Fix) Prevent error when using non-existing layers indexes
- (Fix) Allow use only a mask for operations
- (Fix) Implement the progress bar
- **File formats:**
- (Add) VDA.ZIP (Voxeldance Additive)
- (Improvement) Add a check to global `LightPWM` if 0 it will force to 255
- (Improvement) Add a check to layer `LightPWM` if 0 it will force to 255
- (Add) Allow to save the selected region (ROI) to a image file
- (Update) .NET 5.0.5 to 5.0.6
- (Fix) Getting the transposed rectangle in fliped images are offseting the position by -1
- (Fix) Tools: Hide ROI Region text when empty/not selected
Diffstat (limited to 'UVtools.Core')
-rw-r--r-- | UVtools.Core/About.cs | 5 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 34 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/VDAFile.cs | 432 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/ZCodeFile.cs | 4 | ||||
-rw-r--r-- | UVtools.Core/GCode/GCodeBuilder.cs | 10 | ||||
-rw-r--r-- | UVtools.Core/Layer/Layer.cs | 25 | ||||
-rw-r--r-- | UVtools.Core/Layer/LayerManager.cs | 43 | ||||
-rw-r--r-- | UVtools.Core/Objects/BindableBase.cs | 5 | ||||
-rw-r--r-- | UVtools.Core/Operations/Operation.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationCalibrateExposureFinder.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationLayerArithmetic.cs | 57 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 4 |
12 files changed, 560 insertions, 63 deletions
diff --git a/UVtools.Core/About.cs b/UVtools.Core/About.cs index b6c740d..35373a1 100644 --- a/UVtools.Core/About.cs +++ b/UVtools.Core/About.cs @@ -6,7 +6,9 @@ * of this license document, but changing it is not allowed. */ +using System; using System.IO; +using System.Reflection; namespace UVtools.Core { @@ -19,5 +21,8 @@ namespace UVtools.Core public static string Donate = "https://paypal.me/SkillTournament"; public static string DemoFile = "UVtools_demo_file.sl1"; + + public static Version Version => Assembly.GetExecutingAssembly().GetName().Version; + public static string VersionStr => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); } } diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 0f0ade7..c667d84 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -216,6 +216,7 @@ namespace UVtools.Core.FileFormats new GR1File(), // GR1 Workshop new CXDLPFile(), // Creality Box new LGSFile(), // LGS, LGS30 + new VDAFile(), // VDA new VDTFile(), // VDT new UVJFile(), // UVJ new ImageFile(), // images @@ -367,12 +368,12 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> /// </summary> - public abstract PrintParameterModifier[] PrintParameterModifiers { get; } + public virtual PrintParameterModifier[] PrintParameterModifiers => null; /// <summary> /// Gets the available <see cref="FileFormat.PrintParameterModifier"/> per layer /// </summary> - public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = null; + public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers => null; /// <summary> /// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters @@ -476,7 +477,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the original thumbnail sizes /// </summary> - public abstract Size[] ThumbnailsOriginalSize { get; } + public virtual Size[] ThumbnailsOriginalSize => null; /// <summary> /// Gets the thumbnails for this <see cref="FileFormat"/> @@ -587,12 +588,12 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets or sets the display width in millimeters /// </summary> - public abstract float DisplayWidth { get; set; } + public virtual float DisplayWidth { get; set; } /// <summary> /// Gets or sets the display height in millimeters /// </summary> - public abstract float DisplayHeight { get; set; } + public virtual float DisplayHeight { get; set; } /// <summary> /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation @@ -904,6 +905,8 @@ namespace UVtools.Core.FileFormats set => RaiseAndSet(ref _lightPwm, value); } + public bool CanUseBottomLayerCount => HavePrintParameterModifier(PrintParameterModifier.BottomLayerCount); + public bool CanUseBottomExposureTime => HavePrintParameterModifier(PrintParameterModifier.BottomExposureSeconds); public bool CanUseExposureTime => HavePrintParameterModifier(PrintParameterModifier.ExposureSeconds); public bool CanUseAnyExposureTime => CanUseBottomExposureTime || CanUseExposureTime; @@ -1528,26 +1531,7 @@ namespace UVtools.Core.FileFormats "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath); } - bool reSaveFile = false; - - if(ResolutionX == 0 || ResolutionY == 0) - { - var layer = FirstLayer; - if (layer is not null) - { - using var mat = layer.LayerMat; - - if (mat.Size.HaveZero()) - { - throw new FileLoadException($"File resolution ({Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", fileFullPath); - } - - Resolution = mat.Size; - reSaveFile = true; - } - } - - reSaveFile |= _layerManager.Sanitize(); + bool reSaveFile = _layerManager.Sanitize(); if (reSaveFile) { diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs new file mode 100644 index 0000000..ab6ab3a --- /dev/null +++ b/UVtools.Core/FileFormats/VDAFile.cs @@ -0,0 +1,432 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.OpenSsl; +using UVtools.Core.Extensions; +using UVtools.Core.GCode; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + [Serializable] + [XmlRoot(ElementName = "root")] + public class VDARoot + { + [Serializable] + public class VDAFileInfo + { + [Serializable] + public class VDAVersion + { + public ushort Major { get; set; } = 1; + public ushort Minor { get; set; } = 2; + + } + + [Serializable] + public class VDAWritten + { + [Serializable] + [XmlRoot(ElementName = "By")] + public class VDABy + { + [XmlAttribute] + public string ApplicationName { get; set; } = About.Software; + + [XmlAttribute] + public string ApplicationVersion { get; set; } = About.VersionStr; + + public override string ToString() + { + return $"{ApplicationName} v{ApplicationVersion}"; + } + + public void Reset() + { + ApplicationName = About.Software; + ApplicationVersion = About.VersionStr; + } + } + + public VDABy By { get; set; } = new(); + + public string When { get; set; } = DateTime.Now.ToString("u"); + + public void Reset() + { + When = DateTime.Now.ToString("u"); + By.Reset(); + } + } + + + public VDAVersion Version { get; set; } = new(); + + public VDAWritten Written { get; set; } = new(); + } + + [Serializable] + public class VDASlices + { + public ushort Count { get; set; } = 1; + + [XmlElement("thickness")] + public float LayerHeight { get; set; } + + [XmlElement("startHeight")] + public float StartHeight { get; set; } + + [XmlElement("endHeight")] + public float EndHeight { get; set; } + + [XmlElement("layersCount")] + public uint LayerCount { get; set; } + } + + [Serializable] + public class VDAMachines + { + public string FileType { get; set; } = "ZIP File"; + public string Resolution { get; set; } = "1920*1080P"; + public string PixelXSize { get; set; } = "50um"; + public string PixelYSize { get; set; } = "50um"; + + [XmlElement("Anti-Aliasing")] + public byte AntiAliasing { get; set; } = 1; + + public float XLength { get; set; } + public float YWidth { get; set; } + public float ZHeight { get; set; } + } + + public class VDALayer + { + [XmlElement("Index")] + public uint Index { get; set; } + + [XmlElement("zvalue")] + public float ZPosition { get; set; } + + [XmlElement("filename")] + public string Filename { get; set; } + + public VDALayer() + { + } + + public VDALayer(uint index, float zPosition, string filename) + { + Index = index; + ZPosition = zPosition; + Filename = filename; + } + } + + + public VDAFileInfo FileInfo { get; set; } = new(); + public VDASlices Slices { get; set; } = new(); + public VDAMachines Machines { get; set; } = new(); + public List<VDALayer> Layers { get; set; } = new(); + } + + public class VDAFile : FileFormat + { + #region Constants + + #endregion + + #region Properties + public VDARoot ManifestFile { get; set; } = new (); + + public override FileFormatType FileType => FileFormatType.Archive; + + public override FileExtension[] FileExtensions { get; } = { + new("vda.zip", "Voxeldance Additive Zip") + }; + + public override uint ResolutionX + { + get + { + var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries); + if (resolution.Length < 2) return 0; + uint.TryParse(resolution[0], out var xRes); + return xRes; + } + set + { + ManifestFile.Machines.Resolution = $"{value}*{ResolutionY}P"; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get + { + var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries); + if (resolution.Length < 2) return 0; + resolution[1] = resolution[1].TrimEnd('P'); + uint.TryParse(resolution[1], out var yRes); + return yRes; + } + set + { + ManifestFile.Machines.Resolution = $"{ResolutionX}*{value}P"; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get + { + if (ManifestFile.Machines.XLength > 0) return ManifestFile.Machines.XLength; + + var umStr= ManifestFile.Machines.PixelXSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase); + + if (ushort.TryParse(umStr, out var um) && um > 0) + { + return (float) Math.Round(ResolutionX * um / 1000f, 2); + } + + return ManifestFile.Machines.XLength; + } + set + { + ManifestFile.Machines.XLength = (float) Math.Round(value, 2); + ManifestFile.Machines.PixelXSize = $"{Math.Round(value / ResolutionX * 1000, 2)}um"; + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get + { + if (ManifestFile.Machines.YWidth > 0) return ManifestFile.Machines.YWidth; + + var umStr = ManifestFile.Machines.PixelYSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase); + + if (ushort.TryParse(umStr, out var um) && um > 0) + { + return (float)Math.Round(ResolutionY * um / 1000f, 2); + } + + return ManifestFile.Machines.YWidth; + } + set + { + ManifestFile.Machines.YWidth = (float)Math.Round(value, 2); + ManifestFile.Machines.PixelYSize = $"{Math.Round(value / ResolutionY * 1000, 2)}um"; + RaisePropertyChanged(); + } + } + + public override float MachineZ + { + get => ManifestFile.Machines.ZHeight > 0 ? ManifestFile.Machines.ZHeight : base.MachineZ; + set + { + ManifestFile.Machines.ZHeight = value; + RaisePropertyChanged(); + } + } + + public override byte AntiAliasing + { + get => ManifestFile.Machines.AntiAliasing; + set + { + ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16); + RaisePropertyChanged(); + } + } + + public override float LayerHeight + { + get => ManifestFile.Slices.LayerHeight; + set + { + ManifestFile.Slices.LayerHeight = Layer.RoundHeight(value); + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount; + } + + + public override object[] Configs => new object[] { + ManifestFile.FileInfo.Version, + ManifestFile.FileInfo.Written, + ManifestFile.Machines, + ManifestFile.Slices }; + + #endregion + + #region Constructor + public VDAFile() + { } + #endregion + + #region Methods + + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); + var manifestFilename = Path.GetFileName(fileFullPath). + Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml"). + Replace($".{FileExtensions[0].Extension}", ".xml"); + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + var layer = this[layerIndex]; + var filename = $"{layerIndex + 1}".PadLeft(4, '0') + ".png"; + outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create); + progress++; + } + + UpdateManifest(); + + XmlSerializer serializer = new(ManifestFile.GetType()); + XmlSerializerNamespaces ns = new(); + ns.Add("", ""); + var entry = outputFile.CreateEntry(manifestFilename); + using var stream = entry.Open(); + serializer.Serialize(stream, ManifestFile, ns); + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read)) + { + var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name.EndsWith(".xml")); + if (entry is null) + { + Clear(); + throw new FileLoadException($".xml manifest not found", fileFullPath); + } + + try + { + var serializer = new XmlSerializer(ManifestFile.GetType()); + using var stream = entry.Open(); + ManifestFile = (VDARoot)serializer.Deserialize(stream); + } + catch (Exception e) + { + Clear(); + throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", fileFullPath); + } + + + LayerManager.Init(ManifestFile.Slices.LayerCount); + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + if (progress.Token.IsCancellationRequested) break; + var filename = $"{layerIndex + 1}".PadLeft(4, '0')+".png"; + entry = inputFile.GetEntry(filename); + if (entry is null) + { + Clear(); + throw new FileLoadException($"Layer {filename} not found", fileFullPath); + } + + using var stream = entry.Open(); + this[layerIndex] = new Layer(layerIndex, stream, LayerManager); + + progress++; + } + } + + LayerManager.GetBoundingRectangle(progress); + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update); + bool deleted; + + do + { + deleted = false; + foreach (var zipEntry in outputFile.Entries) + { + if (!zipEntry.Name.EndsWith(".xml")) continue; + zipEntry.Delete(); + deleted = true; + break; + } + } while (deleted); + + var manifestFilename = Path.GetFileName(FileFullPath). + Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml"). + Replace($".{FileExtensions[0].Extension}", ".xml"); + + UpdateManifest(); + + XmlSerializer serializer = new(ManifestFile.GetType()); + XmlSerializerNamespaces ns = new(); + ns.Add("", ""); + var entry = outputFile.CreateEntry(manifestFilename); + using var stream = entry.Open(); + serializer.Serialize(stream, ManifestFile, ns); + } + + public void UpdateManifest() + { + ManifestFile.FileInfo.Written.Reset(); + ManifestFile.Slices.StartHeight = FirstLayer.PositionZ; + ManifestFile.Slices.EndHeight = LastLayer.PositionZ; + ManifestFile.Layers.Clear(); + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + var layer = this[layerIndex]; + ManifestFile.Layers.Add(new VDARoot.VDALayer(layerIndex, layer.PositionZ, layer.FormatFileName(4, false))); + } + } + #endregion + } +} diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 412adb5..698e87f 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -35,7 +35,7 @@ namespace UVtools.Core.FileFormats public class ZcodePrintDevice { [XmlAttribute("z")] - public ushort MachineZ { get; set; } = 220; + public float MachineZ { get; set; } = 220; [XmlAttribute("height")] public uint ResolutionY { get; set; } = 2400; @@ -257,7 +257,7 @@ namespace UVtools.Core.FileFormats get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MachineZ; set { - ManifestFile.Device.MachineZ = (ushort) value; + ManifestFile.Device.MachineZ = value; RaisePropertyChanged(); } } diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 18d5c51..97b4667 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -514,10 +514,10 @@ namespace UVtools.Core.GCode AppendLineIfCanComment(BeginLayerComments, layerIndex, layer.PositionZ); - if (layer.CanExpose) - { - AppendShowImageM6054(GetShowImageString(layerIndex)); - } + //if (layer.CanExpose) + //{ Dont check this for compability + AppendShowImageM6054(GetShowImageString(layerIndex)); + //} if (liftHeight > 0 && liftZPosAbs > layer.PositionZ) { @@ -627,7 +627,7 @@ namespace UVtools.Core.GCode float positionZ = 0; for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++) { - var layer = slicerFile[layerIndex]; + var layer = slicerFile[layerIndex]; if(layer is null) continue; var startStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex)); var endStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex+1)); diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index f9f9695..c1e2fc9 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -202,7 +202,8 @@ namespace UVtools.Core get => _lightPwm; set { - if (value <= 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); + if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); + if (value == 0) value = FileFormat.DefaultLightPWM; RaiseAndSetIfChanged(ref _lightPwm, value); } } @@ -511,11 +512,23 @@ namespace UVtools.Core LightOffDelay = CalculateLightOffDelay(extraTime); } - public string FormatFileName(string name) + public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true) { - return $"{name}{Index.ToString().PadLeft(ParentLayerManager.LayerDigits, '0')}.png"; + var index = Index; + if (!layerIndexZeroStarted) + { + index++; + } + return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}.png"; } + public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true) + => FormatFileName(prepend, ParentLayerManager.LayerDigits, layerIndexZeroStarted); + + public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true) + => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted); + + public Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false) { if (_nonZeroPixelCount > 0 && !reCalculate) @@ -625,8 +638,8 @@ namespace UVtools.Core // 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>(); + List<LayerIssue> result = new(); + List<Point> pixels = new(); @@ -700,7 +713,7 @@ namespace UVtools.Core int y2; - Queue<Point> queue = new Queue<Point>(); + Queue<Point> queue = new(); queue.Enqueue(new Point(x, y)); // Mark this cell as visited visited[x, y] = true; diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index df3a950..e7b9c7d 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -441,6 +441,23 @@ namespace UVtools.Core if (this[layerIndex - 1].PositionZ > this[layerIndex].PositionZ) throw new InvalidDataException($"Layer {layerIndex - 1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n"); } + if (SlicerFile.ResolutionX == 0 || SlicerFile.ResolutionY == 0) + { + var layer = FirstLayer; + if (layer is not null) + { + using var mat = layer.LayerMat; + + if (mat.Size.HaveZero()) + { + throw new FileLoadException($"File resolution ({SlicerFile.Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", SlicerFile.FileFullPath); + } + + SlicerFile.Resolution = mat.Size; + appliedCorrections = true; + } + } + // Fix 0mm positions at layer 0 if (this[0].PositionZ == 0) { @@ -452,6 +469,18 @@ namespace UVtools.Core appliedCorrections = true; } + // Fix LightPWM of 0 + if (SlicerFile.LightPWM == 0) + { + SlicerFile.LightPWM = FileFormat.DefaultLightPWM; + appliedCorrections = true; + } + if (SlicerFile.BottomLightPWM == 0) + { + SlicerFile.BottomLightPWM = FileFormat.DefaultBottomLightPWM; + appliedCorrections = true; + } + return appliedCorrections; } @@ -779,7 +808,7 @@ namespace UVtools.Core if (touchBoundConfig.Enabled) { // TouchingBounds Checker - List<Point> pixels = new List<Point>(); + List<Point> pixels = new(); bool touchTop = layer.BoundingRectangle.Top <= touchBoundConfig.MarginTop; bool touchBottom = layer.BoundingRectangle.Bottom >= image.Height - touchBoundConfig.MarginBottom; bool touchLeft = layer.BoundingRectangle.Left <= touchBoundConfig.MarginLeft; @@ -1113,7 +1142,7 @@ namespace UVtools.Core for (int i = 1; i < numLabels; i++) { - Rectangle rect = new Rectangle( + Rectangle rect = new( (int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Left), (int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Top), (int) ccStats.GetValue(i, (int) ConnectedComponentsTypes.Width), @@ -1128,7 +1157,7 @@ namespace UVtools.Core previousSpan = previousImage.GetPixelSpan<byte>(); } - List<Point> points = new List<Point>(); + List<Point> points = new(); uint pixelsSupportingIsland = 0; for (int y = rect.Y; y < rect.Bottom; y++) @@ -1573,7 +1602,7 @@ namespace UVtools.Core progress ??= new OperationProgress(); progress.Reset("Drawings", (uint) drawings.Count); - ConcurrentDictionary<uint, Mat> modifiedLayers = new ConcurrentDictionary<uint, Mat>(); + ConcurrentDictionary<uint, Mat> modifiedLayers = new(); for (var i = 0; i < drawings.Count; i++) { var operation = drawings[i]; @@ -1648,7 +1677,7 @@ namespace UVtools.Core int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2); int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2); - using (Mat matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter))) + using (Mat matCircleRoi = new(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter))) { using (Mat matCircleMask = matCircleRoi.CloneBlank()) { @@ -1683,9 +1712,9 @@ namespace UVtools.Core int yStart = Math.Max(0, operation.Location.Y - radius); int xStart = Math.Max(0, operation.Location.X - radius); - using (Mat matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter))) + using (Mat matCircleRoi = new(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter))) { - using (Mat matCircleRoiInv = new Mat()) + using (Mat matCircleRoiInv = new()) { CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv); using (Mat matCircleMask = matCircleRoi.CloneBlank()) diff --git a/UVtools.Core/Objects/BindableBase.cs b/UVtools.Core/Objects/BindableBase.cs index 68a7611..9e63a1f 100644 --- a/UVtools.Core/Objects/BindableBase.cs +++ b/UVtools.Core/Objects/BindableBase.cs @@ -21,12 +21,11 @@ namespace UVtools.Core.Objects /// Multicast event for property change notifications. /// </summary> private PropertyChangedEventHandler _propertyChanged; - private List<string> events = new List<string>(); public event PropertyChangedEventHandler PropertyChanged { - add { _propertyChanged += value; events.Add("added"); } - remove { _propertyChanged -= value; events.Add("removed"); } + add => _propertyChanged += value; + remove => _propertyChanged -= value; } /// <summary> diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index b7f7955..5cb7cad 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -235,6 +235,8 @@ namespace UVtools.Core.Operations public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0; + public bool HaveROIorMask => HaveROI || HaveMask; + /// <summary> /// Gets if this operation have been executed once /// </summary> diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 9109e29..9fb8d6b 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -128,7 +128,7 @@ namespace UVtools.Core.Operations private bool _bullsEyeInvertQuadrants = true; private bool _counterTrianglesEnabled = true; - private sbyte _counterTrianglesTipOffset = 1; + private sbyte _counterTrianglesTipOffset = 3; private bool _counterTrianglesFence = false; private bool _patternModel; diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs index 2ea9bc8..721f8c9 100644 --- a/UVtools.Core/Operations/OperationLayerArithmetic.cs +++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; @@ -53,6 +54,8 @@ namespace UVtools.Core.Operations #endregion #region Overrides + + public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None; public override string Title => "Layer arithmetic"; public override string Description => "Perform arithmetic operations over the layers\n" + @@ -63,7 +66,7 @@ namespace UVtools.Core.Operations "Syntax: <set_to_layer_indexes> = <layer_index> <operator> <layer_index>\n" + "When: \"<set_to_layer_indexes> =\" is omitted, the result will assign to the first layer on the sentence.\n\n" + "Example 1: 10+11\n" + - "Example 2: 10,11,12 = 11+12-10*5\n" + + "Example 2: 10,11,12 = 11+12-10*5 Same as: 10:12 = 11+12-10*5\n" + "On example 1 the layer 10 will be set with the result of layer 10 plus layer 11.\n" + "On example 2 the layers 10,11,12 will be set with the result of layer 11 plus 12 minus 10 all multiplied by layer 5.\n\n" + "Note: Calculation are made sequential, math order rules wont apply here."; @@ -87,13 +90,15 @@ namespace UVtools.Core.Operations sb.AppendLine("No layers to assign."); else if (Operations.Count == 0) sb.AppendLine("No operations to perform."); + else if (!IsValid) + sb.AppendLine("The operation will have no impact and will not be performed."); return sb.ToString(); } public override string ToString() { - var result = $"{_sentence}" + LayerRangeString; + var result = $"{_sentence}"; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -106,12 +111,13 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _sentence, value); } [XmlIgnore] - public List<ArithmeticOperation> Operations { get; } = new(); + public List<ArithmeticOperation> Operations { get; private set; } = new(); [XmlIgnore] - public List<uint> SetLayers { get; } = new List<uint>(); + public List<uint> SetLayers { get; private set; } = new(); - public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0; + public bool IsValid => SetLayers.Count > 0 && Operations.Count > 0 && + !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex); #endregion #region Constructor @@ -135,15 +141,31 @@ namespace UVtools.Core.Operations if (splitSentence.Length >= 2) { operations = splitSentence[1]; - var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(','); + var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',', StringSplitOptions.TrimEntries); foreach (var layer in setLayers) { + var rangeSplit = layer.Split(':', StringSplitOptions.TrimEntries); + if (rangeSplit.Length > 1) + { + uint.TryParse(rangeSplit[0].Trim(), out var startLayer); + if (!uint.TryParse(rangeSplit[1].Trim(), out var endLayer)) endLayer = SlicerFile.LastLayerIndex; + for (var index = startLayer; index <= endLayer; index++) + { + if (SetLayers.Contains(index)) continue; + SetLayers.Add(index); + } + continue; + } + if (!uint.TryParse(layer.Trim(), out var layerIndex)) continue; if (SetLayers.Contains(layerIndex)) continue; SetLayers.Add(layerIndex); } } + SetLayers = SetLayers.Where(layerIndex => layerIndex <= SlicerFile.LastLayerIndex).ToList(); + SetLayers.Sort(); + operations = operations.Replace(" ", string.Empty); if (string.IsNullOrWhiteSpace(operations)) return false; @@ -208,8 +230,10 @@ namespace UVtools.Core.Operations } } - if (Operations.Count == 0) return false; - if (SetLayers.Count == 0) + Operations = Operations.Where(op => op.LayerIndex <= SlicerFile.LastLayerIndex).ToList(); + + //if (Operations.Count == 0) return false; + if (SetLayers.Count == 0 && Operations.Count > 0) { SetLayers.Add(Operations[0].LayerIndex); } @@ -223,12 +247,15 @@ namespace UVtools.Core.Operations using var result = SlicerFile[Operations[0].LayerIndex].LayerMat; using var resultRoi = GetRoiOrDefault(result); + using var imageMask = GetMask(resultRoi); + + progress.ItemCount = (uint) Operations.Count; for (int i = 1; i < Operations.Count; i++) { progress.Token.ThrowIfCancellationRequested(); using var image = SlicerFile[Operations[i].LayerIndex].LayerMat; var imageRoi = GetRoiOrDefault(image); - using var imageMask = GetMask(image); + switch (Operations[i - 1].Operator) { case LayerArithmeticOperators.Add: @@ -256,20 +283,26 @@ namespace UVtools.Core.Operations CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi); break; } + + progress++; } + progress.Reset("Applied layers", (uint) SetLayers.Count); Parallel.ForEach(SetLayers, layerIndex => { if (progress.Token.IsCancellationRequested) return; - if (Operations.Count == 1 && HaveROI) + progress.LockAndIncrement(); + if (Operations.Count == 1 || HaveROIorMask) { - var mat = SlicerFile[layerIndex].LayerMat; + using var mat = SlicerFile[layerIndex].LayerMat; var matRoi = GetRoiOrDefault(mat); - using var imageMask = GetMask(mat); resultRoi.CopyTo(matRoi, imageMask); SlicerFile[layerIndex].LayerMat = mat; return; } + + //ApplyMask(mat, resultRoi, imageMask); + SlicerFile[layerIndex].LayerMat = result; }); diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index d3eac6a..46b05c3 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.11.2</Version> + <Version>2.12.0</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> @@ -49,7 +49,7 @@ <PackageReference Include="AnimatedGif" Version="1.0.5" /> <PackageReference Include="BinarySerializer" Version="8.6.0" /> <PackageReference Include="Emgu.CV" Version="4.5.1.4349" /> - <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-2.final" /> + <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-3.final" /> <PackageReference Include="morelinq" Version="3.3.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.10" /> |