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); }