public static bool MergePerimeterOverlaps(this Polygons perimetersIn, long overlapMergeAmount_um, out Polygons separatedPolygons, bool pathIsClosed = true) { // if the path is wound CW separatedPolygons = new Polygons(); long cleanDistance_um = overlapMergeAmount_um / 40; var perimeters = Clipper.CleanPolygons(perimetersIn, cleanDistance_um); if (perimeters.Count == 0) { return(false); } bool pathWasOptimized = false; // Set all the paths to have the width we are starting width (as we will be changing some of them) foreach (var perimeter in perimeters) { for (int i = 0; i < perimeter.Count; i++) { perimeter[i] = new IntPoint(perimeter[i]) { Width = overlapMergeAmount_um }; } } perimeters = perimeters.MakeCloseSegmentsMergable(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(perimeters, pathIsClosed); var markedAltered = new Altered[polySegments.Count]; var minimumLengthToCreateSquared = overlapMergeAmount_um; 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); } } if (polySegments.Count > 0) { // add the end point currentPolygon.Add(polySegments[polySegments.Count - 1].End); } if (pathWasOptimized && Math.Abs(perimeters.PolygonLength() - separatedPolygons.PolygonLength(false)) < overlapMergeAmount_um * 2) { return(false); } return(pathWasOptimized); }
/// <summary> /// Create the list of polygon segments (not closed) that represent the parts of the source polygons that are close (almost touching). /// </summary> /// <param name="polygons"></param> /// <param name="overlapMergeAmount">If edges under consideration, are this distance or less appart (but greater than minimumRequiredWidth) they will generate edges</param> /// <param name="minimumRequiredWidth">If the distance between edges is less this they will not be generated. This lets us avoid considering very very thin lines.</param> /// <param name="onlyMergeLines">The output segments that are calculated</param> /// <param name="pathIsClosed">Is the source path closed (does not contain the last edge but assumes it).</param> /// <returns></returns> public static bool FindThinLines(this Polygons polygons, long overlapMergeAmount, long minimumRequiredWidth, out Polygons onlyMergeLines, bool pathIsClosed = true) { bool pathHasMergeLines = false; polygons = MakeCloseSegmentsMergable(polygons, overlapMergeAmount, pathIsClosed); // make a copy that has every point duplicated (so that we have them as segments). List <Segment> polySegments = Segment.ConvertToSegments(polygons); Altered[] markedAltered = new Altered[polySegments.Count]; var touchingEnumerator = new CloseSegmentsIterator(polySegments, overlapMergeAmount); 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) { // 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) { // move the first segments points to the average of the merge positions long startEndWidth = Math.Abs((polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length()); long endStartWidth = Math.Abs((polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length()); long width = Math.Min(startEndWidth, endStartWidth); if (width > minimumRequiredWidth) { // We need to check if the new start position is on the inside of the curve. We can only add thin lines on the insides of our exisiting curves. IntPoint newStartPosition = (polySegments[firstSegmentIndex].Start + polySegments[checkSegmentIndex].End) / 2; // the start; IntPoint newStartDirection = newStartPosition - polySegments[firstSegmentIndex].Start; IntPoint normalLeft = (polySegments[firstSegmentIndex].End - polySegments[firstSegmentIndex].Start).GetPerpendicularLeft(); long dotProduct = normalLeft.Dot(newStartDirection); if (dotProduct > 0) { pathHasMergeLines = true; polySegments[firstSegmentIndex].Start = newStartPosition; polySegments[firstSegmentIndex].Start.Width = width; polySegments[firstSegmentIndex].End = (polySegments[firstSegmentIndex].End + polySegments[checkSegmentIndex].Start) / 2; // the end 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--) { // remove every segment that has not been merged if (markedAltered[segmentIndex] != Altered.merged) { polySegments.RemoveAt(segmentIndex); } } // go through the polySegments and create a new polygon for every connected set of segments onlyMergeLines = new Polygons(); Polygon currentPolygon = new Polygon(); onlyMergeLines.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(); onlyMergeLines.Add(currentPolygon); } } // add the end point if (polySegments.Count > 0) { currentPolygon.Add(polySegments[polySegments.Count - 1].End); } //long cleanDistance = overlapMergeAmount / 40; //Clipper.CleanPolygons(onlyMergeLines, cleanDistance); return(pathHasMergeLines); }
public static bool MergePerimeterOverlaps(this Polygon perimeter, long overlapMergeAmount_um, out Polygons separatedPolygons, bool pathIsClosed = true) { 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 pathWasOptomized = false; for (int i = 0; i < perimeter.Count; i++) { perimeter[i] = new IntPoint(perimeter[i]) { Width = overlapMergeAmount_um }; } perimeter = QTPolygonExtensions.MakeCloseSegmentsMergable(perimeter, overlapMergeAmount_um, pathIsClosed); // make a copy that has every point duplicated (so that we have them as segments). List <Segment> polySegments = Segment.ConvertToSegments(perimeter, pathIsClosed); Altered[] markedAltered = new Altered[polySegments.Count]; 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 considre the merge if the directions of the lines are towards eachother var firstSegmentDirection = polySegments[firstSegmentIndex].End - polySegments[firstSegmentIndex].Start; var checkSegmentDirection = polySegments[checkSegmentIndex].End - polySegments[checkSegmentIndex].Start; if (firstSegmentDirection.Dot(checkSegmentDirection) > 0) { continue; } pathWasOptomized = true; // move the first segments points to the average of the merge positions long startEndWidth = Math.Abs((polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length()); long endStartWidth = Math.Abs((polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length()); long width = Math.Min(startEndWidth, endStartWidth) + overlapMergeAmount_um; polySegments[firstSegmentIndex].Start = (polySegments[firstSegmentIndex].Start + polySegments[checkSegmentIndex].End) / 2; // the start polySegments[firstSegmentIndex].Start.Width = width; polySegments[firstSegmentIndex].End = (polySegments[firstSegmentIndex].End + polySegments[checkSegmentIndex].Start) / 2; // the end 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 Polygon 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); return(pathWasOptomized); }