Beispiel #1
0
        /// <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">The polygons to search for thin lines</param>
        /// <param name="overlapMergeAmount">If edges under consideration, are this distance or less apart (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 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>If thin lines were detected</returns>
        public static bool FindThinLines(this Polygons polygons, long overlapMergeAmount, long minimumRequiredWidth, out Polygons onlyMergeLines, bool pathIsClosed = true)
        {
            polygons = Clipper.CleanPolygons(polygons, overlapMergeAmount / 8);
            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);

            var 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 existing 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();
            var 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);
        }