diff --git a/UVtools.Core/EmguCV/EmguContour.cs b/UVtools.Core/EmguCV/EmguContour.cs index 5e5bd10..b4af097 100644 --- a/UVtools.Core/EmguCV/EmguContour.cs +++ b/UVtools.Core/EmguCV/EmguContour.cs @@ -35,7 +35,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< #region Members - private VectorOfPoint _points = null!; + private VectorOfPoint _points; private Rectangle? _bounds; private RotatedRect? _boundsBestFit; private CircleF? _minEnclosingCircle; @@ -106,7 +106,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< (int)Math.Round(Moments.M01 / Moments.M00)); /// - /// Gets or sets the contour + /// Gets or sets the contour points /// public VectorOfPoint Points { @@ -224,7 +224,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< #region Static methods public static Point GetCentroid(VectorOfPoint points) { - if (points is null || points.Length == 0) return EmguExtensions.AnchorCenter; + if (points.Length == 0) return EmguExtensions.AnchorCenter; using var moments = CvInvoke.Moments(points); return moments.M00 == 0 ? EmguExtensions.AnchorCenter : new Point( @@ -235,6 +235,13 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< #region Implementations + public EmguContour Clone() + { + var clone = (EmguContour)MemberwiseClone(); + clone._points = new VectorOfPoint(_points.ToArray()); + return clone; + } + public IEnumerator GetEnumerator() { return (IEnumerator) _points.ToArray().GetEnumerator(); @@ -254,8 +261,9 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< public void Dispose() { - _points?.Dispose(); + _points.Dispose(); _moments?.Dispose(); + _moments = null; } #endregion @@ -295,6 +303,4 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< return x._area.CompareTo(y._area); } #endregion - - } \ No newline at end of file diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index 164fe64..bc82109 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -44,6 +44,21 @@ public class EmguContours : IReadOnlyList, IDisposable public EmguContour this[int index] => _contours[index]; + private EmguContours(int count, int[,] hierarchy) + { + _contours = new EmguContour[count]; + Hierarchy = hierarchy; + } + + public EmguContours(Point[][] points) + { + _contours = new EmguContour[points.Length]; + for (int i = 0; i < points.Length; i++) + { + _contours[i] = new EmguContour(points[i]); + } + } + public EmguContours(VectorOfVectorOfPoint vectorOfPointsOfPoints) { _contours = new EmguContour[vectorOfPointsOfPoints.Size]; @@ -53,6 +68,11 @@ public class EmguContours : IReadOnlyList, IDisposable } } + public EmguContours(Point[][] points, int[,] hierarchy) : this(points) + { + Hierarchy = hierarchy; + } + public EmguContours(VectorOfVectorOfPoint vectorOfPointsOfPoints, int[,] hierarchy) : this(vectorOfPointsOfPoints) { Hierarchy = hierarchy; @@ -96,6 +116,18 @@ public class EmguContours : IReadOnlyList, IDisposable return items; } + public EmguContours Clone() + { + var clone = new EmguContours(_contours.Length, Hierarchy); + + for (int i = 0; i < _contours.Length; i++) + { + clone._contours[i] = this[i].Clone(); + } + + return clone; + } + public void Dispose() { foreach (var contour in _contours) @@ -104,6 +136,17 @@ public class EmguContours : IReadOnlyList, IDisposable } } + + + + + + + + + + + /// /// Gets contours inside a point /// @@ -331,10 +374,11 @@ public class EmguContours : IReadOnlyList, IDisposable { var contour1Rect = CvInvoke.BoundingRectangle(contour1[0]); var contour2Rect = CvInvoke.BoundingRectangle(contour2[0]); - + /* early exit if the bounding rectangles don't intersect */ if (!contour1Rect.IntersectsWith(contour2Rect)) return 0; - var totalRect = Rectangle.Union(contour1Rect, contour2Rect); + //var totalRect = Rectangle.Union(contour1Rect, contour2Rect); + var totalRect = RectangleExtensions.SmallestRectangle(contour1Rect, contour2Rect); using var contour1Mat = EmguExtensions.InitMat(totalRect.Size); using var contour2Mat = EmguExtensions.InitMat(totalRect.Size); diff --git a/UVtools.Core/Extensions/RectangleExtensions.cs b/UVtools.Core/Extensions/RectangleExtensions.cs index 05fbb41..eb1174f 100644 --- a/UVtools.Core/Extensions/RectangleExtensions.cs +++ b/UVtools.Core/Extensions/RectangleExtensions.cs @@ -6,6 +6,7 @@ * of this license document, but changing it is not allowed. */ using System.Drawing; +using System.Linq; namespace UVtools.Core.Extensions; @@ -25,4 +26,32 @@ public static class RectangleExtensions public static Rectangle OffsetBy(this Rectangle src, Point position) => src.OffsetBy(position.X, position.Y); + /// + /// Gets the smallest rectangle among all rectangles + /// + /// + /// The smallest rectangle + public static Rectangle SmallestRectangle(params Rectangle[] rectangles) => rectangles.MinBy(rectangle => rectangle.Area()); + + /// + /// Gets the smallest rectangle among all rectangles + /// + /// + /// The smallest rectangle + public static RectangleF SmallestRectangle(params RectangleF[] rectangles) => rectangles.MinBy(rectangle => rectangle.Area()); + + /// + /// Gets the largest rectangle among all rectangles + /// + /// + /// The largest rectangle + public static Rectangle LargestRectangle(params Rectangle[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Area()); + + /// + /// Gets the largest rectangle among all rectangles + /// + /// + /// The largest rectangle + public static RectangleF LargestRectangle(params RectangleF[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Area()); + } \ No newline at end of file diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index e2dba00..ae65ea8 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -83,6 +83,8 @@ public class Layer : BindableBase, IEquatable, IEquatable private byte _lightPWM = FileFormat.DefaultLightPWM; private float _materialMilliliters; + private EmguContours? _contours; + #endregion #region Properties @@ -794,6 +796,8 @@ public class Layer : BindableBase, IEquatable, IEquatable _compressedBytes = value; IsModified = true; SlicerFile.BoundingRectangle = Rectangle.Empty; + _contours?.Dispose(); + _contours = null; RaisePropertyChanged(); RaisePropertyChanged(nameof(HaveImage)); } @@ -1027,6 +1031,31 @@ public class Layer : BindableBase, IEquatable, IEquatable /// public bool IsUsingTSMC => LiftHeight2 > 0 || RetractHeight2 > 0; + /// + /// Gets tree contours cache for this layer, however should call instead with instance + /// + public EmguContours Contours + { + get + { + if (_contours is null) + { + using var mat = LayerMat; + _contours = new EmguContours(mat); + } + + return _contours; + } + } + + /// + /// Gets tree contours cache for this layer + /// + public EmguContours GetContours(IInputOutputArray mat) + { + return _contours ??= new EmguContours(mat); + } + #endregion #region Constructor @@ -1694,6 +1723,7 @@ public class Layer : BindableBase, IEquatable, IEquatable layer.ResolutionX = _resolutionX; layer.ResolutionY = _resolutionY; layer.CompressedBytes = _compressedBytes?.ToArray(); + layer._contours = _contours?.Clone(); layer.BoundingRectangle = _boundingRectangle; layer.NonZeroPixelCount = _nonZeroPixelCount; } @@ -1702,6 +1732,7 @@ public class Layer : BindableBase, IEquatable, IEquatable { var layer = (Layer)MemberwiseClone(); layer._compressedBytes = _compressedBytes?.ToArray(); + layer._contours = _contours?.Clone(); //Debug.WriteLine(ReferenceEquals(_compressedBytes, layer.CompressedBytes)); return layer; /*return new (_index, CompressedBytes?.ToArray()!, SlicerFile) diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml index 7fd2f5a..3236aba 100644 --- a/documentation/UVtools.Core.xml +++ b/documentation/UVtools.Core.xml @@ -139,7 +139,7 @@ - Gets or sets the contour + Gets or sets the contour points @@ -1172,6 +1172,34 @@ Optional prepend file name + + + Gets the smallest rectangle among all rectangles + + + The smallest rectangle + + + + Gets the smallest rectangle among all rectangles + + + The smallest rectangle + + + + Gets the largest rectangle among all rectangles + + + The largest rectangle + + + + Gets the largest rectangle among all rectangles + + + The largest rectangle + Gets if this size have a zero value on width or height @@ -2104,6 +2132,11 @@ Slicer representation + + + Gets the decimal precision for display properties + + Enumeration of file format types @@ -5050,6 +5083,16 @@ True if this layer is using TSMC values, otherwise false + + + Gets tree contours cache for this layer, however should call instead with instance + + + + + Gets tree contours cache for this layer + + Reset all parameters to the default values from the global parameters