public static Polygons MakeCloseSegmentsMergable(this Polygons polygonsToSplit, long distanceNeedingAdd, bool pathsAreClosed = true) { Polygons splitPolygons = new Polygons(); for (int i = 0; i < polygonsToSplit.Count; i++) { Polygon accumulatedSplits = polygonsToSplit[i]; for (int j = 0; j < polygonsToSplit.Count; j++) { accumulatedSplits = QTPolygonExtensions.MakeCloseSegmentsMergable(accumulatedSplits, polygonsToSplit[j], distanceNeedingAdd, pathsAreClosed); } splitPolygons.Add(accumulatedSplits); } return(splitPolygons); }
public static bool MergePerimeterOverlaps(this Polygon perimeter, long overlapMergeAmount_um, out Polygons separatedPolygons, bool pathIsClosed = true) { // if the path is wound CW separatedPolygons = new Polygons(); long cleanDistance_um = overlapMergeAmount_um / 40; Polygons cleanedPolygs = Clipper.CleanPolygons(new Polygons() { perimeter }, cleanDistance_um); perimeter = cleanedPolygs[0]; if (perimeter.Count == 0) { return(false); } bool pathWasOptimized = false; for (int i = 0; i < perimeter.Count; i++) { perimeter[i] = new IntPoint(perimeter[i]) { Width = overlapMergeAmount_um }; } perimeter = QTPolygonExtensions.MakeCloseSegmentsMergable(perimeter, overlapMergeAmount_um * 3 / 4, pathIsClosed); // make a copy that has every point duplicated (so that we have them as segments). List <Segment> polySegments = Segment.ConvertToSegments(perimeter, pathIsClosed); var markedAltered = new Altered[polySegments.Count]; var minimumLengthToCreateSquared = overlapMergeAmount_um / 10; minimumLengthToCreateSquared *= minimumLengthToCreateSquared; var touchingEnumerator = new CloseSegmentsIterator(polySegments, overlapMergeAmount_um); int segmentCount = polySegments.Count; // now walk every segment and check if there is another segment that is similar enough to merge them together for (int firstSegmentIndex = 0; firstSegmentIndex < segmentCount; firstSegmentIndex++) { foreach (int checkSegmentIndex in touchingEnumerator.GetTouching(firstSegmentIndex, segmentCount)) { // The first point of start and the last point of check (the path will be coming back on itself). long startDelta = (polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length(); // if the segments are similar enough if (startDelta < overlapMergeAmount_um) { // The last point of start and the first point of check (the path will be coming back on itself). long endDelta = (polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length(); if (endDelta < overlapMergeAmount_um) { // only consider the merge if the directions of the lines are towards each other var firstSegmentDirection = polySegments[firstSegmentIndex].End - polySegments[firstSegmentIndex].Start; var checkSegmentDirection = polySegments[checkSegmentIndex].End - polySegments[checkSegmentIndex].Start; if (firstSegmentDirection.Dot(checkSegmentDirection) > 0) { continue; } // get the line width long startEndWidth = (polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length(); long endStartWidth = (polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length(); long width = Math.Min(startEndWidth, endStartWidth) + overlapMergeAmount_um; // check if we extrude enough to consider doing this merge var segmentStart = (polySegments[firstSegmentIndex].Start + polySegments[checkSegmentIndex].End) / 2; var segmentEnd = (polySegments[firstSegmentIndex].End + polySegments[checkSegmentIndex].Start) / 2; if ((segmentStart - segmentEnd).LengthSquared() < minimumLengthToCreateSquared) { continue; } pathWasOptimized = true; // move the first segments points to the average of the merge positions polySegments[firstSegmentIndex].Start = segmentStart; polySegments[firstSegmentIndex].Start.Width = width; polySegments[firstSegmentIndex].End = segmentEnd; polySegments[firstSegmentIndex].End.Width = width; markedAltered[firstSegmentIndex] = Altered.Merged; // mark this segment for removal markedAltered[checkSegmentIndex] = Altered.Remove; // We only expect to find one match for each segment, so move on to the next segment break; } } } } // remove the marked segments for (int segmentIndex = segmentCount - 1; segmentIndex >= 0; segmentIndex--) { if (markedAltered[segmentIndex] == Altered.Remove) { polySegments.RemoveAt(segmentIndex); } } // go through the polySegments and create a new polygon for every connected set of segments var currentPolygon = new Polygon(); separatedPolygons.Add(currentPolygon); // put in the first point for (int segmentIndex = 0; segmentIndex < polySegments.Count; segmentIndex++) { // add the start point currentPolygon.Add(polySegments[segmentIndex].Start); // if the next segment is not connected to this one if (segmentIndex < polySegments.Count - 1 && polySegments[segmentIndex].End != polySegments[segmentIndex + 1].Start) { // add the end point currentPolygon.Add(polySegments[segmentIndex].End); // create a new polygon currentPolygon = new Polygon(); separatedPolygons.Add(currentPolygon); } } // add the end point currentPolygon.Add(polySegments[polySegments.Count - 1].End); if (pathWasOptimized && Math.Abs(perimeter.PolygonLength() - separatedPolygons.PolygonLength(false)) < overlapMergeAmount_um * 2) { return(false); } return(pathWasOptimized); }