diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-04-06 20:17:48 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-04-06 20:17:48 +0300 |
commit | 331d37be7c8e3dfcbf08a4896fff1103b519d977 (patch) | |
tree | 8ad40f70b9e1943ea5c1fbf8a86f28e696e887a5 /UVtools.Core | |
parent | 7538d49acafb7667efd3f38fd84e7c5f7de8f241 (diff) |
v3.2.2 - Birthday release 🎁 (2 years old) 🥳v3.2.2
- **Settings:**
- (Add) Remove source file after automatic conversion (#444)
- (Add) Remove source file after manual conversion (#444)
- (Add) **Average resin bottle cost:** The average cost per one resin bottle of 1000ml.
Used to calculate the material cost when the file lacks that information.
Use 0 to disable this feature and only show the cost if file have that information.
If this value is changed, you need to reload the current file to update the cost.
- (Change) Move "Expand and show tool descriptions by default" to From `General` to `Tools` tab (Setting will reset to default)
- **File formats:**
- (Add) Property `StartingMaterialMilliliters`: Gets the starting material milliliters when the file was loaded
- (Add) Property `StartingMaterialCost`: Gets the starting material cost when the file was loaded
- (Add) Property `MaterialMilliliterCost`: Gets the material cost per one milliliter
- (Improvement) Update `MaterialCost` when `MaterialMilliliters` changes (#449)
- **Raft relief:**
- (Add) Linked lines: Remove the raft, keep supports and link them with lines
- (Improvement) Change the supports detection parameters to be more effective and precise on detect the starting layer
- (Fix) Brightness percentage not getting updated
- (Fix) Remove anti-aliased edges from Tabs
- (Improvement) Core: Minor clean-up
- (Fix) Issue repair error when 'Auto repair layers and issues on file load' is enabled (#446)
- (Fix) UI: Selecting a object with ROI when flip is verically, will cause 1px up-shift on the preview
- (Fix) macOS permission error due loss of attributes on the build script, WSL have bug with mv, using ln&rm instead
Diffstat (limited to 'UVtools.Core')
-rw-r--r-- | UVtools.Core/About.cs | 147 | ||||
-rw-r--r-- | UVtools.Core/CoreSettings.cs | 8 | ||||
-rw-r--r-- | UVtools.Core/EmguCV/EmguContours.cs | 68 | ||||
-rw-r--r-- | UVtools.Core/Extensions/DateTimeExtensions.cs | 32 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 21 | ||||
-rw-r--r-- | UVtools.Core/GCode/GCodeBuilder.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/Layers/Layer.cs | 36 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationLayerExportImage.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationRaftRelief.cs | 143 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationRepairLayers.cs | 19 | ||||
-rw-r--r-- | UVtools.Core/RELEASE_NOTES.md | 12 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 8 |
12 files changed, 415 insertions, 83 deletions
diff --git a/UVtools.Core/About.cs b/UVtools.Core/About.cs index db116bf..b41c27b 100644 --- a/UVtools.Core/About.cs +++ b/UVtools.Core/About.cs @@ -7,27 +7,162 @@ */ using System; +using System.IO; +using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; namespace UVtools.Core; public static class About { public const string Software = "UVtools"; + public static Version Version => CoreAssembly.GetName().Version!; + public static string VersionStr => Version.ToString(3); + public static string VersionArch => $"{VersionStr} {RuntimeInformation.ProcessArchitecture}"; public static string SoftwareWithVersion => $"{Software} v{VersionStr}"; + public static string SoftwareWithVersionArch => $"{Software} v{VersionArch}"; public const string Author = "Tiago Conceição"; - public const string Company = "PTRTECH"; public const string License = "GNU Affero General Public License v3.0 (AGPL)"; public const string LicenseUrl = "https://github.com/sn4k3/UVtools/blob/master/LICENSE"; public const string Website = "https://github.com/sn4k3/UVtools"; public const string Donate = "https://paypal.me/SkillTournament"; public const string Sponsor = "https://github.com/sponsors/sn4k3"; - public const string DemoFile = "UVtools_demo_file.sl1"; + #region Assembly properties + public static Assembly CoreAssembly => Assembly.GetExecutingAssembly(); - public static Version Version => CoreAssembly.GetName().Version!; - public static string VersionStr => Version.ToString(3); - public static string Arch => Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; + public static string AssemblyVersion => CoreAssembly.GetName().Version?.ToString()!; + + public static string AssemblyName => Assembly.GetExecutingAssembly().GetName().Name!; + + public static string AssemblyTitle + { + get + { + var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + var titleAttribute = (AssemblyTitleAttribute)attributes[0]; + if (titleAttribute.Title != string.Empty) + { + return titleAttribute.Title; + } + } + return Path.GetFileNameWithoutExtension(CoreAssembly.Location); + } + } + + public static string AssemblyDescription + { + get + { + var attributes = CoreAssembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + if (attributes.Length == 0) + { + return string.Empty; + } + + var description = ((AssemblyDescriptionAttribute)attributes[0]).Description + $"{Environment.NewLine}{Environment.NewLine}Available File Formats:"; + + return FileFormat.AvailableFormats.SelectMany(fileFormat => fileFormat.FileExtensions).Aggregate(description, (current, fileExtension) => current + $"{Environment.NewLine}- {fileExtension.Description} (.{fileExtension.Extension})"); + } + } + + public static string AssemblyProduct + { + get + { + var attributes = CoreAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false); + return attributes.Length == 0 ? string.Empty : ((AssemblyProductAttribute)attributes[0]).Product; + } + } + + public static string AssemblyCopyright + { + get + { + var attributes = CoreAssembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + return attributes.Length == 0 ? string.Empty : ((AssemblyCopyrightAttribute)attributes[0]).Copyright; + } + } + + public static string AssemblyCompany + { + get + { + var attributes = CoreAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + return attributes.Length == 0 ? string.Empty : ((AssemblyCompanyAttribute)attributes[0]).Company; + } + } + #endregion + + public static string SystemBits => Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; + + /// <summary> + /// Gets UVtools born date and time + /// </summary> + public static DateTime Born => new(2020, 4, 6, 20, 33, 14); + + /// <summary> + /// Gets UVtools years + /// </summary> + public static int YearsOld => Born.Age(); + + /// <summary> + /// Return full age in a readable string + /// </summary> + public static string AgeStr + { + get + { + var sb = new StringBuilder($"{YearsOld} years"); + var born = Born; + var now = DateTime.Now; + + var months = 12 + now.Month - born.Month + (now.Day >= born.Day ? 0 : -1); + if (months >= 12) months -= 12; + if (months > 0) sb.Append($", {months} month(s)"); + + + + var days = 31 + now.Day - born.Day; + if (days >= 31) days -= 31; + if (days > 0) sb.Append($", {days} day(s)"); + + var hours = 12 + now.Hour - born.Hour; + if (hours >= 12) hours -= 12; + if (hours > 0) sb.Append($", {hours} hour(s)"); + + var minutes = 60 + now.Minute - born.Minute; + if (minutes >= 60) minutes -= 60; + if (minutes > 0) sb.Append($", {minutes} minutes(s)"); + + var seconds = 60 + now.Second - born.Second; + if (seconds >= 60) seconds -= 60; + if (seconds > 0) sb.Append($", {seconds} seconds(s)"); + + + return sb.ToString(); + } + } + + /// <summary> + /// Checks if today is UVtools birthday + /// </summary> + public static bool IsBirthday + { + get + { + var born = Born; + var now = DateTime.Now; + return born.Month == now.Month && born.Day == now.Day; + } + } + + public const string DemoFile = "UVtools_demo_file.sl1"; - public static Assembly CoreAssembly => Assembly.GetExecutingAssembly(); }
\ No newline at end of file diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs index f048af2..10246be 100644 --- a/UVtools.Core/CoreSettings.cs +++ b/UVtools.Core/CoreSettings.cs @@ -71,7 +71,13 @@ public static class CoreSettings /// <summary> /// Gets or sets the default compression type for layers /// </summary> - public static Layer.LayerCompressionCodec DefaultLayerCompressionCodec { get; set; } = Layer.LayerCompressionCodec.Png; + public static LayerCompressionCodec DefaultLayerCompressionCodec { get; set; } = LayerCompressionCodec.Png; + + /// <summary> + /// <para>The average resin 1000ml bottle cost, to use when bottle cost is not available.</para> + /// <para>Use 0 to disable.</para> + /// </summary> + public static float AverageResin1000MlBottleCost = 60f; /// <summary> /// Gets the default folder to save the settings diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index 958cfd8..d8c0639 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -6,10 +6,13 @@ * of this license document, but changing it is not allowed. */ +using System; +using System.Collections; using Emgu.CV; using Emgu.CV.Util; using System.Collections.Generic; using System.Drawing; +using System.Linq; using System.Threading.Tasks; using UVtools.Core.Extensions; @@ -19,8 +22,71 @@ namespace UVtools.Core.EmguCV; /// Utility methods for contour handling. /// Use only with Tree type /// </summary> -public static class EmguContours +public class EmguContours : IReadOnlyList<EmguContour>, IDisposable { + private readonly EmguContour[] _contours; + + public IEnumerator<EmguContour> GetEnumerator() + { + return ((IEnumerable<EmguContour>)_contours).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _contours.GetEnumerator(); + } + + public int Count => _contours.Length; + + + + public EmguContour this[int index] => _contours[index]; + + public EmguContours(VectorOfVectorOfPoint vectorOfPointsOfPoints) + { + _contours = new EmguContour[vectorOfPointsOfPoints.Size]; + for (int i = 0; i < _contours.Length; i++) + { + _contours[i] = new EmguContour(vectorOfPointsOfPoints[i]); + } + } + + public (int Index, EmguContour Contour, double Distance)[][] CalculateCentroidDistances(bool includeOwn = false, bool sortByDistance = true) + { + var items = new (int Index, EmguContour Contour, double Distance)[Count][]; + for (int i = 0; i < Count; i++) + { + items[i] = new (int Index, EmguContour Contour, double Distance)[Count-1]; + int count = 0; + for (int x = 0; x < Count; x++) + { + if (x == i) + { + if (includeOwn) + { + items[i][count] = new(x, this[x], 0); + } + continue; + } + items[i][count] = new (x, this[x], PointExtensions.FindLength(this[i].Centroid, this[x].Centroid)); + + count++; + } + + if(sortByDistance) items[i] = items[i].OrderBy(tuple => tuple.Distance).ToArray(); + } + + return items; + } + + public void Dispose() + { + foreach (var contour in _contours) + { + contour.Dispose(); + } + } + /// <summary> /// Gets contours inside a point /// </summary> diff --git a/UVtools.Core/Extensions/DateTimeExtensions.cs b/UVtools.Core/Extensions/DateTimeExtensions.cs index ebb132e..9186d5d 100644 --- a/UVtools.Core/Extensions/DateTimeExtensions.cs +++ b/UVtools.Core/Extensions/DateTimeExtensions.cs @@ -47,4 +47,36 @@ public static class DateTimeExtensions { return DateTime.UnixEpoch.AddMinutes(minutes); } + + /// <summary> + /// Calculates the age in years of the current System.DateTime object today. + /// </summary> + /// <param name="birthDate">The date of birth</param> + /// <returns>Age in years today. 0 is returned for a future date of birth.</returns> + public static int Age(this DateTime birthDate) + { + return Age(birthDate, DateTime.Today); + } + + /// <summary> + /// Calculates the age in years of the current System.DateTime object on a later date. + /// </summary> + /// <param name="birthDate">The date of birth</param> + /// <param name="laterDate">The date on which to calculate the age.</param> + /// <returns>Age in years on a later day. 0 is returned as minimum.</returns> + public static int Age(this DateTime birthDate, DateTime laterDate) + { + var age = laterDate.Year - birthDate.Year; + + if (age > 0) + { + age -= Convert.ToInt32(laterDate.Date < birthDate.Date.AddYears(age)); + } + else + { + age = 0; + } + + return age; + } }
\ No newline at end of file diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 12ad9e4..6102aef 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -2725,7 +2725,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor } /// <summary> - /// Gets the starting material milliliters when the file loaded + /// Gets the starting material milliliters when the file was loaded /// </summary> public float StartingMaterialMilliliters { get; private set; } @@ -2749,7 +2749,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor if (StartingMaterialMilliliters > 0 && StartingMaterialCost > 0) { - MaterialCost = _materialMilliliters * StartingMaterialCost / StartingMaterialMilliliters; + MaterialCost = GetMaterialCostPer(_materialMilliliters); } //RaisePropertyChanged(nameof(MaterialCost)); } @@ -2768,7 +2768,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor } /// <summary> - /// Gets the starting material cost when the file loaded + /// Gets the starting material cost when the file was loaded /// </summary> public float StartingMaterialCost { get; private set; } @@ -2782,6 +2782,13 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor } /// <summary> + /// Gets the material cost per one milliliter + /// </summary> + public float MaterialMilliliterCost => StartingMaterialMilliliters > 0 ? StartingMaterialCost / StartingMaterialMilliliters : 0; + + public float GetMaterialCostPer(float milliliters, byte roundDigits = 3) => (float)Math.Round(MaterialMilliliterCost * milliliters, roundDigits); + + /// <summary> /// Gets the material name /// </summary> public virtual string? MaterialName @@ -3313,7 +3320,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor IsModified = false; StartingMaterialMilliliters = MaterialMilliliters; StartingMaterialCost = MaterialCost; - + if (StartingMaterialCost <= 0) + { + StartingMaterialCost = StartingMaterialMilliliters * CoreSettings.AverageResin1000MlBottleCost / 1000f; + MaterialCost = StartingMaterialCost; + } progress.ThrowIfCancellationRequested(); @@ -4629,7 +4640,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor /// </summary> /// <param name="newCodec">The new method to change to</param> /// <param name="progress"></param> - public void ChangeLayersCompressionMethod(Layer.LayerCompressionCodec newCodec, OperationProgress? progress = null) + public void ChangeLayersCompressionMethod(LayerCompressionCodec newCodec, OperationProgress? progress = null) { progress ??= new OperationProgress($"Changing layers compression codec to {newCodec}"); progress.Reset("Layers", LayerCount); diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 98b224f..1cacb93 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -309,7 +309,7 @@ public class GCodeBuilder : BindableBase public void AppendUVtools() { - AppendComment($"Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow}"); + AppendComment($"Generated by {About.Software} v{About.VersionStr} {About.SystemBits} @ {DateTime.UtcNow}"); } public void AppendStartGCode() diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index 2015ac7..4db7a9e 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -25,29 +25,29 @@ using UVtools.Core.Operations; namespace UVtools.Core.Layers; +#region Enums + +public enum LayerCompressionCodec : byte +{ + [Description("PNG: Compression=High Speed=Slow (Use with low RAM)")] + Png, + [Description("GZip: Compression=Medium Speed=Medium (Optimal)")] + GZip, + [Description("Deflate: Compression=Medium Speed=Medium (Optimal)")] + Deflate, + [Description("LZ4: Compression=Low Speed=Fast (Use with high RAM)")] + Lz4, + //[Description("None: Compression=None Speed=Fastest (Your soul belongs to RAM)")] + //None +} + +#endregion + /// <summary> /// Represent a Layer /// </summary> public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint> { - #region Enums - - public enum LayerCompressionCodec : byte - { - [Description("PNG: Compression=High Speed=Slow (Use with low RAM)")] - Png, - [Description("GZip: Compression=Medium Speed=Medium (Optimal)")] - GZip, - [Description("Deflate: Compression=Medium Speed=Medium (Optimal)")] - Deflate, - [Description("LZ4: Compression=Low Speed=Fast (Use with high RAM)")] - Lz4, - //[Description("None: Compression=None Speed=Fastest (Your soul belongs to RAM)")] - //None - } - - #endregion - #region Constants public const byte HeightPrecision = 3; public const decimal HeightPrecisionIncrement = 0.001M; diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs index 918c66b..5fbc7ca 100644 --- a/UVtools.Core/Operations/OperationLayerExportImage.cs +++ b/UVtools.Core/Operations/OperationLayerExportImage.cs @@ -216,7 +216,7 @@ public sealed class OperationLayerExportImage : Operation using TextWriter tw = new StreamWriter(fileFullPath); tw.WriteLine("<!--"); - tw.WriteLine($"# Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow} #"); + tw.WriteLine($"# Generated by {About.Software} v{About.VersionStr} {About.SystemBits} @ {DateTime.UtcNow} #"); tw.WriteLine($"File: {SlicerFile.Filename}"); tw.WriteLine($"{SlicerFile[layerIndex].ToString().Replace(", ", "\n")}"); tw.WriteLine("-->"); diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index 9725317..3b4ccef 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -30,6 +30,9 @@ public class OperationRaftRelief : Operation [Description("Relief: Drill raft to relief pressure and remove some mass")] Relief, + [Description("Linked lines: Remove the raft, keep supports and link them with lines")] + LinkedLines, + [Description("Dimming: Darkens the raft to cure it less")] Dimming, @@ -45,12 +48,15 @@ public class OperationRaftRelief : Operation private RaftReliefTypes _reliefType = RaftReliefTypes.Relief; private uint _maskLayerIndex; private byte _ignoreFirstLayers; - private byte _brightness; + private byte _lowBrightness; private byte _dilateIterations = 15;// +/- 1.5mm radius private byte _wallMargin = 40; // +/- 2mm private byte _holeDiameter = 80; // +/- 4mm private byte _holeSpacing = 40; // +/- 2mm - private byte _tabBrightness = byte.MaxValue; + private byte _linkedLineThickness = 26; + private byte _linkedMinimumLinks = 4; + private bool _linkedExternalSupports = true; + private byte _highBrightness = byte.MaxValue; private ushort _tabTriangleBase = 200; private ushort _tabTriangleHeight = 250; @@ -60,7 +66,7 @@ public class OperationRaftRelief : Operation public override string IconClass => "fas fa-bowling-ball"; public override string Title => "Raft relief"; public override string Description => - "Relief raft by adding holes in between to reduce FEP suction, save resin and easier to remove the prints."; + "Relief raft with a strategy to remove mass, reduce FEP suction, spare resin and easier to remove the prints."; public override string ConfirmationText => $"relief the raft"; @@ -70,8 +76,7 @@ public class OperationRaftRelief : Operation public override string ProgressAction => "Relieved layers"; - public override LayerRangeSelection StartLayerRangeSelection => - LayerRangeSelection.None; + public override LayerRangeSelection StartLayerRangeSelection => LayerRangeSelection.None; public override string? ValidateInternally() { @@ -80,7 +85,11 @@ public class OperationRaftRelief : Operation { if(_tabTriangleHeight == 0) sb.AppendLine("The tab height can't be 0"); if(_tabTriangleBase == 0) sb.AppendLine("The tab base can't be 0"); - if(_tabBrightness == 0) sb.AppendLine("The tab brightness can't be 0"); + if(_highBrightness == 0) sb.AppendLine("The tab brightness can't be 0"); + } + else if (_reliefType == RaftReliefTypes.LinkedLines) + { + if(_linkedLineThickness < 4) sb.AppendLine("The link thickness can't be less than 4"); } return sb.ToString(); @@ -88,7 +97,7 @@ public class OperationRaftRelief : Operation public override string ToString() { - var result = $"[{_reliefType}] [Mask layer: {_maskLayerIndex}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]"; + var result = $"[{_reliefType}] [Mask layer: {_maskLayerIndex}] [Ignore: {_ignoreFirstLayers}] [B: {_lowBrightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]"; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -110,6 +119,7 @@ public class OperationRaftRelief : Operation { if(!RaiseAndSetIfChanged(ref _reliefType, value)) return; RaisePropertyChanged(nameof(IsRelief)); + RaisePropertyChanged(nameof(IsLinkedLines)); RaisePropertyChanged(nameof(IsDimming)); RaisePropertyChanged(nameof(IsDecimate)); RaisePropertyChanged(nameof(IsTabs)); @@ -118,6 +128,7 @@ public class OperationRaftRelief : Operation } public bool IsRelief => _reliefType == RaftReliefTypes.Relief; + public bool IsLinkedLines => _reliefType == RaftReliefTypes.LinkedLines; public bool IsDimming => _reliefType == RaftReliefTypes.Dimming; public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate; public bool IsTabs => _reliefType == RaftReliefTypes.Tabs; @@ -134,17 +145,27 @@ public class OperationRaftRelief : Operation set => RaiseAndSetIfChanged(ref _ignoreFirstLayers, value); } - public byte Brightness + public byte LowBrightness { - get => _brightness; + get => _lowBrightness; set { - if (!RaiseAndSetIfChanged(ref _brightness, value)) return; + if (!RaiseAndSetIfChanged(ref _lowBrightness, value)) return; RaisePropertyChanged(nameof(BrightnessPercent)); } } - public decimal BrightnessPercent => Math.Round((_reliefType == RaftReliefTypes.Tabs ? _tabBrightness : _brightness) * 100 / 255M, 2); + public byte HighBrightness + { + get => _highBrightness; + set + { + if(!RaiseAndSetIfChanged(ref _highBrightness, Math.Max((byte) 1, value))) return; + RaisePropertyChanged(nameof(BrightnessPercent)); + } + } + + public decimal BrightnessPercent => Math.Round((_reliefType is RaftReliefTypes.LinkedLines or RaftReliefTypes.Tabs ? _highBrightness : _lowBrightness) * 100 / 255M, 2); public byte DilateIterations { @@ -170,10 +191,22 @@ public class OperationRaftRelief : Operation set => RaiseAndSetIfChanged(ref _holeSpacing, value); } - public byte TabBrightness + public byte LinkedLineThickness { - get => _tabBrightness; - set => RaiseAndSetIfChanged(ref _tabBrightness, Math.Max((byte)1, value)); + get => _linkedLineThickness; + set => RaiseAndSetIfChanged(ref _linkedLineThickness, Math.Max((byte)4, value)); + } + + public byte LinkedMinimumLinks + { + get => _linkedMinimumLinks; + set => RaiseAndSetIfChanged(ref _linkedMinimumLinks, value); + } + + public bool LinkedExternalSupports + { + get => _linkedExternalSupports; + set => RaiseAndSetIfChanged(ref _linkedExternalSupports, value); } public ushort TabTriangleBase @@ -198,7 +231,7 @@ public class OperationRaftRelief : Operation protected bool Equals(OperationRaftRelief other) { - return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing && _tabTriangleBase == other._tabTriangleBase && _tabTriangleHeight == other._tabTriangleHeight; + return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _lowBrightness == other._lowBrightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing && _linkedLineThickness == other._linkedLineThickness && _linkedMinimumLinks == other._linkedMinimumLinks && _linkedExternalSupports == other._linkedExternalSupports && _highBrightness == other._highBrightness && _tabTriangleBase == other._tabTriangleBase && _tabTriangleHeight == other._tabTriangleHeight; } public override bool Equals(object? obj) @@ -215,11 +248,15 @@ public class OperationRaftRelief : Operation hashCode.Add((int) _reliefType); hashCode.Add(_maskLayerIndex); hashCode.Add(_ignoreFirstLayers); - hashCode.Add(_brightness); + hashCode.Add(_lowBrightness); hashCode.Add(_dilateIterations); hashCode.Add(_wallMargin); hashCode.Add(_holeDiameter); hashCode.Add(_holeSpacing); + hashCode.Add(_linkedLineThickness); + hashCode.Add(_linkedMinimumLinks); + hashCode.Add(_linkedExternalSupports); + hashCode.Add(_highBrightness); hashCode.Add(_tabTriangleBase); hashCode.Add(_tabTriangleHeight); return hashCode.ToHashCode(); @@ -248,7 +285,8 @@ public class OperationRaftRelief : Operation { progress.ThrowIfCancellationRequested(); supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat); - var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 200); + //var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 255); // OLD + var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.GradientAlt, 1.5, 25, 300, 0.80, 5, 255); if (circles.Length >= minSupportsRequired) break; supportsMat.Dispose(); @@ -263,20 +301,21 @@ public class OperationRaftRelief : Operation if (supportsMat is null || /*firstSupportLayerIndex == 0 ||*/ _ignoreFirstLayers >= firstSupportLayerIndex) return false; Mat? patternMat = null; + using var supportsMatOriginal = supportsMat.Clone(); - if (DilateIterations > 0) + if (_dilateIterations > 0) { CvInvoke.Dilate(supportsMat, supportsMat, CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1)), - new Point(-1, -1), DilateIterations, BorderType.Reflect101, new MCvScalar()); + new Point(-1, -1), _dilateIterations, BorderType.Reflect101, new MCvScalar()); } - var color = new MCvScalar(255 - Brightness); + var color = new MCvScalar(255 - _lowBrightness); switch (ReliefType) { case RaftReliefTypes.Relief: - patternMat = EmguExtensions.InitMat(supportsMat.Size); + patternMat = supportsMat.NewBlank(); int shapeSize = HoleDiameter + HoleSpacing; using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize))) { @@ -296,6 +335,61 @@ public class OperationRaftRelief : Operation } break; + case RaftReliefTypes.LinkedLines: + { + using var contours = new EmguContours(supportsMatOriginal.FindContours()); + using var supportsRedraw = _linkedExternalSupports ? supportsMatOriginal.Clone() : null; + using var supportsBrightnessCorrection = _highBrightness < byte.MaxValue ? supportsMat.Clone() : null; + + var centroidDistance = contours.CalculateCentroidDistances(false, true); + + var links = Math.Min(_linkedMinimumLinks, contours.Count-1); + var linkColor = new MCvScalar(_highBrightness); + + //var listPoints = new List<Point>(); + + for (int i = 0; i < contours.Count; i++) + { + if(contours[i].Centroid.IsAnyNegative()) continue; + //listPoints.Add(contours[i].Centroid); + + // Link all centroids to each other to calculate the external contour + if (_linkedExternalSupports) + { + for (int x = 0; x < contours.Count; x++) + { + if (x == i) continue; + if (contours[x].Centroid.IsAnyNegative()) continue; + CvInvoke.Line(supportsRedraw, contours[i].Centroid, contours[x].Centroid, linkColor, 4); + } + } + + for (int link = 0; link < links; link++) + { + CvInvoke.Line(supportsMat, contours[i].Centroid, centroidDistance[i][link].Contour.Centroid, linkColor, _linkedLineThickness); + } + } + + + //CvInvoke.Polylines(supportsMat, listPoints.ToArray(), false, linkColor, _linkedLineThickness); + // Link external centroids + if (_linkedExternalSupports) + { + using var externalContours = supportsRedraw!.FindContours(RetrType.External); + CvInvoke.DrawContours(supportsMat, externalContours, -1, linkColor, _linkedLineThickness); + } + + // Fix original supports brightness + if (_highBrightness < byte.MaxValue) + { + supportsBrightnessCorrection!.CopyTo(supportsMat, supportsBrightnessCorrection); + } + + // Close minor holes, round imperfections, stronger joints + CvInvoke.MorphologyEx(supportsMat, supportsMat, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, new Point(-1, -1), 1, BorderType.Reflect101, default); + + break; + } case RaftReliefTypes.Dimming: patternMat = EmguExtensions.InitMat(supportsMat.Size, color); break; @@ -324,6 +418,7 @@ public class OperationRaftRelief : Operation } break; + case RaftReliefTypes.LinkedLines: case RaftReliefTypes.Decimate: supportsMat.CopyTo(target); break; @@ -348,7 +443,7 @@ public class OperationRaftRelief : Operation new Point(-1, 0), // Left }; - var color = new MCvScalar(_tabBrightness); + var color = new MCvScalar(_highBrightness); for (var i = 0; i < contours.Size; i++) { @@ -419,9 +514,9 @@ public class OperationRaftRelief : Operation break; } - CvInvoke.Ellipse(mat, new Point(x, y), new Size(triangleBaseRadius, (int)(triangleBaseRadius / 1.5)), 90 * dir, 0, 180, EmguExtensions.WhiteColor, -1, LineType.AntiAlias); + CvInvoke.Ellipse(mat, new Point(x, y), new Size(triangleBaseRadius, (int)(triangleBaseRadius / 1.5)), 90 * dir, 0, 180, EmguExtensions.WhiteColor, -1); using var vec = new VectorOfPoint(polygon); - CvInvoke.FillPoly(mat, vec, color, LineType.AntiAlias); + CvInvoke.FillPoly(mat, vec, color); } } diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index f1b5bec..f839390 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -159,8 +159,7 @@ public class OperationRepairLayers : Operation set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value); } - [XmlIgnore] - public IslandDetectionConfiguration? IslandDetectionConfig { get; set; } + [XmlIgnore] public IslandDetectionConfiguration IslandDetectionConfig { get; set; } = new(); #endregion @@ -205,8 +204,8 @@ public class OperationRepairLayers : Operation var issues = SlicerFile.IssueManager.GetVisible().ToList(); // Remove islands if (//Issues is not null - IslandDetectionConfig is not null - && _repairIslands + //IslandDetectionConfig is not null + _repairIslands && _removeIslandsBelowEqualPixelCount > 0 && _removeIslandsRecursiveIterations != 1) { @@ -282,7 +281,7 @@ public class OperationRepairLayers : Operation if (islandsToProcess.Count == 0) { - var islandConfig = IslandDetectionConfig?.Clone() ?? new IslandDetectionConfiguration(); + var islandConfig = IslandDetectionConfig.Clone(); var overhangConfig = new OverhangDetectionConfiguration(false); var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false); var printHeightConfig = new PrintHeightDetectionConfiguration(false); @@ -292,12 +291,12 @@ public class OperationRepairLayers : Operation islandConfig.Enabled = true; islandsToProcess = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig, progress); - islandsToProcess?.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue)); + islandsToProcess.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue)); } - var issuesGroup = IssueManager.GetIssuesBy(islandsToProcess!, MainIssue.IssueType.Island).GroupBy(issue => issue.LayerIndex); + var issuesGroup = IssueManager.GetIssuesBy(islandsToProcess, MainIssue.IssueType.Island).GroupBy(issue => issue.LayerIndex); - progress.Reset("Attempt to attach islands below", (uint) islandsToProcess!.Count); + progress.Reset("Attempt to attach islands below", (uint) islandsToProcess.Count); var sync = new object(); Parallel.ForEach(issuesGroup, CoreSettings.GetParallelOptions(progress), group => { @@ -310,7 +309,7 @@ public class OperationRepairLayers : Operation for (var layerIndex = startLayer+1; layerIndex >= lowestPossibleLayer; layerIndex--) { - Debug.WriteLine(layerIndex); + //Debug.WriteLine(layerIndex); Monitor.Enter(SlicerFile[layerIndex].Mutex); matCache.Add((uint) layerIndex, SlicerFile[layerIndex].LayerMat); matCacheModified.Add((uint) layerIndex, false); @@ -319,7 +318,7 @@ public class OperationRepairLayers : Operation foreach (IssueOfPoints issue in group) { int foundAt = startLayer == 0 ? 0 : - 1; - var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IslandDetectionConfig!.RequiredPixelsToSupportMultiplier); + var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IslandDetectionConfig.RequiredPixelsToSupportMultiplier); for (var layerIndex = startLayer; layerIndex >= lowestPossibleLayer && foundAt < 0; layerIndex--) { diff --git a/UVtools.Core/RELEASE_NOTES.md b/UVtools.Core/RELEASE_NOTES.md deleted file mode 100644 index 5efb19e..0000000 --- a/UVtools.Core/RELEASE_NOTES.md +++ /dev/null @@ -1,12 +0,0 @@ -- **AnyCubic file format:** - - (Fix) Lift height and speed are not being correctly set on old version, keeping a constant value (#441) - - (Fix) Retract speed getter was not return value from TSMC if using version 516 -- **Tool - Infill:** - - (Add) Waves infill type - - (Add) Concentric infill type - - (Add) Gyroid infill type (#443) - - (Change) Increase the default spacing from 200px to 300px - - (Improvement) Fastter infill processing by use the model bounds -- (Add) `FormatSpeedUnit` property to file formats to get the speed unit which the file use internally -- (Fix) UI: ROI rectangle can overlap scroll bars while selecting - diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 5321ef9..69928f1 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -5,12 +5,12 @@ <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageLicenseFile>LICENSE</PackageLicenseFile> <Company>PTRTECH</Company> - <Authors>Tiago Conceição</Authors> + <Authors>Tiago Conceição, sn4k3</Authors> <RepositoryType>Git</RepositoryType> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>3.2.1</Version> + <Version>3.2.2</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> @@ -50,7 +50,7 @@ </None> <None Include="..\README.md"> <Pack>True</Pack> - <PackagePath>\</PackagePath> + <PackagePath></PackagePath> </None> <None Include="..\UVtools.CAD\UVtools.png"> <Pack>True</Pack> @@ -74,7 +74,7 @@ </ItemGroup> <Target Name="PreparePackageReleaseNotesFromFile" BeforeTargets="GenerateNuspec"> - <ReadLinesFromFile File="RELEASE_NOTES.md"> + <ReadLinesFromFile File="..\RELEASE_NOTES.md"> <Output TaskParameter="Lines" ItemName="ReleaseNoteLines" /> </ReadLinesFromFile> <PropertyGroup> |