From 400f46202d14349bb9de8aa7296c7b95b9a5678b Mon Sep 17 00:00:00 2001 From: tslater2006 Date: Thu, 16 Sep 2021 22:40:36 -0500 Subject: [PATCH] Refactor suction cup grouping logic This commit removes the Parent/Child issue structures that were added to Issue, and instead opts for a more lean approach to determining the layers affected by an operation. Instead of building out the entire parent/child tree for ever single suction cup issue, we know only calculate what we need when we need it. During a repair operation, we calculate only the suction cup issues that have no overlapping suction cup issue below it. For "delete issue" behavior we only calculate the suction cup issues that overlap the selected on in both directions (following that direction until no more overlaps are found). --- UVtools.Core/Layer/LayerIssue.cs | 3 - UVtools.Core/Layer/LayerManager.cs | 17 +---- .../Operations/OperationRepairLayers.cs | 59 +++++++++++------- UVtools.WPF/MainWindow.Issues.cs | 62 +++++++++++++------ 4 files changed, 82 insertions(+), 59 deletions(-) diff --git a/UVtools.Core/Layer/LayerIssue.cs b/UVtools.Core/Layer/LayerIssue.cs index 6430ac4..18897e4 100644 --- a/UVtools.Core/Layer/LayerIssue.cs +++ b/UVtools.Core/Layer/LayerIssue.cs @@ -301,9 +301,6 @@ namespace UVtools.Core /// public Point[][] Contours { get; init; } - public LayerIssue ParentIssue = null; - public List ChildIssues = null; - public int PixelsCount { get diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 7c7e3fe..277c973 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -1493,22 +1493,7 @@ namespace UVtools.Core var suctionTrapArea = EmguContours.GetContourArea(trap); if (suctionTrapArea < minimumSuctionArea) continue; var rect = CvInvoke.BoundingRectangle(trap[0]); - var newIssue = new LayerIssue(this[layerIndex], LayerIssue.IssueType.SuctionCup, trap.ToArrayOfArray(), rect) { Area = suctionTrapArea }; - - if (layerIndex < LayerCount - 1 && suctionTraps[layerIndex + 1] is not null) - { - - foreach (var issue in result.Where(issue => issue.LayerIndex == layerIndex + 1 && issue.Type == LayerIssue.IssueType.SuctionCup)) - { - if (EmguContours.CheckContoursIntersect(new VectorOfVectorOfPoint(issue.Contours), trap)) - { - issue.ChildIssues ??= new List(); - issue.ChildIssues.Add(newIssue); - newIssue.ParentIssue = issue; - } - } - } - AddIssue(newIssue); + AddIssue(new LayerIssue(this[layerIndex], LayerIssue.IssueType.SuctionCup, trap.ToArrayOfArray(), rect) { Area = suctionTrapArea }); } } } diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index 49a148b..31a1680 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -19,6 +19,7 @@ using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Util; +using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.PixelEditor; @@ -409,6 +410,33 @@ namespace UVtools.Core.Operations if (_repairSuctionCups) { + /* build out parent/child relationships between all detected suction cups */ + + ConcurrentBag bottomSuctionIssues = new(); + + var layerCount = SlicerFile.LayerCount; + Parallel.ForEach(Issues.Where(issue => issue.Type == LayerIssue.IssueType.SuctionCup), issue => { + + if (issue.LayerIndex == 0) + { + bottomSuctionIssues.Add(issue); + return; + } + bool overlapFound = false; + + foreach(var candidate in Issues.Where(candidate => candidate.LayerIndex == issue.LayerIndex - 1 && candidate.Type == LayerIssue.IssueType.SuctionCup)) + { + if (EmguContours.CheckContoursIntersect(new VectorOfVectorOfPoint(issue.Contours), new VectorOfVectorOfPoint(candidate.Contours))) { + overlapFound = true; + break; + } + } + if (!overlapFound) + { + bottomSuctionIssues.Add(issue); + } + }); + (bool canDrill, Point location) GetDrillLocation(LayerIssue issue, int diameter) { var centroid = EmguCV.EmguContour.GetCentroid(new VectorOfPoint(issue.Contours[0])); @@ -420,7 +448,7 @@ namespace UVtools.Core.Operations CvInvoke.DrawContours(contourMat, new VectorOfVectorOfPoint(issue.Contours), -1, EmguExtensions.WhiteColor, -1, Emgu.CV.CvEnum.LineType.EightConnected, null, int.MaxValue, inverseOffset); - CvInvoke.Circle(circleCheck, new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), 5, EmguExtensions.WhiteColor, -1); + CvInvoke.Circle(circleCheck, new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), diameter, EmguExtensions.WhiteColor, -1); CvInvoke.BitwiseAnd(circleCheck, contourMat, overlapCheck); @@ -437,30 +465,19 @@ namespace UVtools.Core.Operations } } - ConcurrentBag drillOps = new ConcurrentBag(); - var suctionReliefSize = (int)Math.Max(SlicerFile.PpmmMax * 1, 20); - Parallel.For(LayerIndexStart, LayerIndexEnd, CoreSettings.ParallelOptions, layerIndex => + List drillOps = new List(); + var suctionReliefSize = (int)Math.Max(SlicerFile.PpmmMax * .5, 15); + /* for each suction cup issue that is an initial layer */ + foreach (var issue in bottomSuctionIssues) { - if (progress.Token.IsCancellationRequested) return; - var layer = SlicerFile[layerIndex]; - - - if (Issues is not null) + var drillPoint = GetDrillLocation(issue, suctionReliefSize); + if (drillPoint.canDrill) { - /* for each suction cup issue that is an initial layer */ - foreach (var issue in Issues.Where(issue => issue.LayerIndex == layerIndex && issue.Type == LayerIssue.IssueType.SuctionCup && (issue.ChildIssues is null))) - { - var drillPoint = GetDrillLocation(issue, suctionReliefSize); - if (drillPoint.canDrill) - { - drillOps.Add(new PixelDrainHole((uint)layerIndex, drillPoint.location, (byte)suctionReliefSize)); - } - } + drillOps.Add(new PixelDrainHole(issue.LayerIndex, drillPoint.location, (byte)suctionReliefSize)); } + } - }); - - SlicerFile.LayerManager.DrawModifications(drillOps.ToList(), progress); + SlicerFile.LayerManager.DrawModifications(drillOps, progress); } diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index b5e8642..fc617d4 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -19,9 +19,11 @@ using Emgu.CV.Structure; using Emgu.CV.Util; using MessageBox.Avalonia.Enums; using UVtools.Core; +using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.Operations; using UVtools.WPF.Extensions; +using static UVtools.Core.LayerIssue; using Brushes = Avalonia.Media.Brushes; namespace UVtools.WPF @@ -83,6 +85,25 @@ namespace UVtools.WPF IssueSelectedIndex++; } + public List GetOverlappingIssues(LayerIssue targetIssue, int indexOffset) + { + List retValue = new(); + + var targetLayerIndex = targetIssue.LayerIndex + indexOffset; + if (targetLayerIndex > SlicerFile.LayerCount - 1 || targetLayerIndex < 0) return retValue; + + foreach (var candidate in Issues.Where(candidate => candidate.LayerIndex == targetLayerIndex && candidate.Type == LayerIssue.IssueType.SuctionCup)) + { + if (EmguContours.CheckContoursIntersect(new VectorOfVectorOfPoint(targetIssue.Contours), new VectorOfVectorOfPoint(candidate.Contours))) + { + retValue.Add(candidate); + break; + } + } + + return retValue; + } + public async void OnClickIssueRemove() { if (IssuesGrid.SelectedItems.Count == 0) return; @@ -209,8 +230,6 @@ namespace UVtools.WPF issue.Type != LayerIssue.IssueType.EmptyLayer && issue.Type != LayerIssue.IssueType.SuctionCup) continue; - issueRemoveList.Add(issue); - if (issue.Type == LayerIssue.IssueType.Island) { var nextLayer = issue.Layer.Index + 1; @@ -221,30 +240,35 @@ namespace UVtools.WPF if (issue.Type == LayerIssue.IssueType.SuctionCup) { - var currentIssue = issue.ParentIssue ?? issue; - /* find the parent */ - while(currentIssue.ParentIssue != null) - { - currentIssue = currentIssue.ParentIssue; - } + if (issueRemoveList.Contains(issue)) continue; - /* now currentIssue is the top most parent for the range */ - Stack children = new Stack(); - children.Push(currentIssue); - while(children.Count > 0) + Stack upDirection = new Stack(); + Stack downDirection = new Stack(); + upDirection.Push(issue); + downDirection.Push(issue); + + while (upDirection.Count > 0) { - var child = children.Pop(); - issueRemoveList.Add(child); - if (child.ChildIssues != null) + var current = upDirection.Pop(); + foreach(var iss in GetOverlappingIssues(current,1)) { - foreach (var c in child.ChildIssues) - { - children.Push(c); - } + upDirection.Push(iss); + issueRemoveList.Add(iss); } } + while(downDirection.Count > 0) + { + var current = downDirection.Pop(); + foreach (var iss in GetOverlappingIssues(current, -1)) + { + downDirection.Push(iss); + issueRemoveList.Add(iss); + } + } + } + issueRemoveList.Add(issue); //Issues.Remove(issue); }