예제 #1
0
        /// <inheritdoc />
        public int FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule)
        {
            this.EnsureInternalPathsInitalized();

            int totalAdded = 0;

            Orientation[] orientations = ArrayPool <Orientation> .Shared.Rent(buffer.Length); // the largest number of intersections of any sub path of the set is the max size with need for this buffer.

            Span <Orientation> orientationsSpan = orientations;

            try
            {
                foreach (var ip in this.internalPaths)
                {
                    Span <PointF>      subBuffer           = buffer.Slice(totalAdded);
                    Span <Orientation> subOrientationsSpan = orientationsSpan.Slice(totalAdded);

                    var position = ip.FindIntersectionsWithOrientation(start, end, subBuffer, subOrientationsSpan);
                    totalAdded += position;
                }

                Span <float> distances = stackalloc float[totalAdded];
                for (int i = 0; i < totalAdded; i++)
                {
                    distances[i] = Vector2.DistanceSquared(start, buffer[i]);
                }

                var activeBuffer           = buffer.Slice(0, totalAdded);
                var activeOrientationsSpan = orientationsSpan.Slice(0, totalAdded);
                QuickSort.Sort(distances, activeBuffer, activeOrientationsSpan);

                if (intersectionRule == IntersectionRule.Nonzero)
                {
                    totalAdded = InternalPath.ApplyNonZeroIntersectionRules(activeBuffer, activeOrientationsSpan);
                }
            }
            finally
            {
                ArrayPool <Orientation> .Shared.Return(orientations);
            }

            return(totalAdded);
        }
        /// <inheritdoc />
        public int FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule)
        {
            int totalAdded = 0;

            for (int i = 0; i < this.paths.Length; i++)
            {
                Span <PointF> subBuffer = buffer.Slice(totalAdded);
                int           added     = this.paths[i].FindIntersections(start, end, subBuffer, intersectionRule);
                totalAdded += added;
            }

            Span <float> distances = stackalloc float[totalAdded];

            for (int i = 0; i < totalAdded; i++)
            {
                distances[i] = Vector2.DistanceSquared(start, buffer[i]);
            }

            QuickSort.Sort(distances, buffer.Slice(0, totalAdded));

            return(totalAdded);
        }
예제 #3
0
        /// <summary>
        /// Based on a line described by <paramref name="start" /> and <paramref name="end" />
        /// populates a buffer for all points on the path that the line intersects.
        /// </summary>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        /// <param name="buffer">The buffer.</param>
        /// <param name="intersectionRule">Intersection rule types</param>
        /// <returns>number of intersections hit</returns>
        public int FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule)
        {
            if (this.points.Length < 2)
            {
                return(0);
            }

            int count = buffer.Length;

            this.ClampPoints(ref start, ref end);

            var target = new Segment(start, end);

            int polyCorners = this.points.Length;

            if (!this.closedPath)
            {
                polyCorners -= 1;
            }

            int     position  = 0;
            Vector2 lastPoint = MaxVector;

            PassPointData[] precaclulate = ArrayPool <PassPointData> .Shared.Rent(this.points.Length);

            Orientation[] orientations = ArrayPool <Orientation> .Shared.Rent(this.points.Length);

            Span <Orientation>   orientationsSpan = orientations.AsSpan(0, this.points.Length);
            Span <PassPointData> precaclulateSpan = precaclulate.AsSpan(0, this.points.Length);

            try
            {
                // pre calculate relative orientations X places ahead and behind
                Vector2     startToEnd           = end - start;
                Orientation prevOrientation      = CalulateOrientation(startToEnd, this.points[polyCorners - 1].Point - end);
                Orientation nextOrientation      = CalulateOrientation(startToEnd, this.points[0].Point - end);
                Orientation nextPlus1Orientation = CalulateOrientation(startToEnd, this.points[1].Point - end);

                // iterate over all points and precalculate data about each, pre cacluating it relative orientation
                for (int i = 0; i < polyCorners && count > 0; i++)
                {
                    ref Segment edge = ref this.points[i].Segment;

                    // shift all orientations along but one place and fill in the last one
                    Orientation pointOrientation = nextOrientation;
                    nextOrientation      = nextPlus1Orientation;
                    nextPlus1Orientation = CalulateOrientation(startToEnd, this.points[WrapArrayIndex(i + 2, this.points.Length)].Point - end);

                    // should this point cause the last matched point to be excluded
                    bool removeLastIntersection = nextOrientation == Orientation.Colinear &&
                                                  pointOrientation == Orientation.Colinear &&
                                                  nextPlus1Orientation != prevOrientation &&
                                                  (this.closedPath || i > 0) &&
                                                  (IsOnSegment(target, edge.Start) || IsOnSegment(target, edge.End));

                    // is there any chance the segments will intersection (do their bounding boxes touch)
                    bool doIntersect = false;
                    if (pointOrientation == Orientation.Colinear || pointOrientation != nextOrientation)
                    {
                        doIntersect = (edge.Min.X - Epsilon) <= target.Max.X &&
                                      (edge.Max.X + Epsilon) >= target.Min.X &&
                                      (edge.Min.Y - Epsilon) <= target.Max.Y &&
                                      (edge.Max.Y + Epsilon) >= target.Min.Y;
                    }

                    precaclulateSpan[i] = new PassPointData
                    {
                        RemoveLastIntersectionAndSkip = removeLastIntersection,
                        RelativeOrientation           = pointOrientation,
                        DoIntersect = doIntersect
                    };

                    prevOrientation = pointOrientation;
                }

                // seed the last point for deduping at begining of closed line
                if (this.closedPath)
                {
                    int prev = polyCorners - 1;

                    if (precaclulateSpan[prev].DoIntersect)
                    {
                        lastPoint = FindIntersection(this.points[prev].Segment, target);
                    }
                }

                for (int i = 0; i < polyCorners && count > 0; i++)
                {
                    int next = WrapArrayIndex(i + 1, this.points.Length);

                    if (precaclulateSpan[i].RemoveLastIntersectionAndSkip)
                    {
                        if (position > 0)
                        {
                            position--;
                            count++;
                        }

                        continue;
                    }

                    if (precaclulateSpan[i].DoIntersect)
                    {
                        Vector2 point = FindIntersection(this.points[i].Segment, target);
                        if (point != MaxVector)
                        {
                            if (lastPoint.Equivelent(point, Epsilon2))
                            {
                                lastPoint = MaxVector;

                                int last = WrapArrayIndex(i - 1 + polyCorners, polyCorners);

                                // hit the same point a second time do we need to remove the old one if just clipping
                                if (this.points[next].Point.Equivelent(point, Epsilon))
                                {
                                    next = i;
                                }

                                if (this.points[last].Point.Equivelent(point, Epsilon))
                                {
                                    last = i;
                                }

                                Orientation side  = precaclulateSpan[next].RelativeOrientation;
                                Orientation side2 = precaclulateSpan[last].RelativeOrientation;

                                if (side != side2)
                                {
                                    // differnet side we skip adding as we are passing through it
                                    continue;
                                }
                            }

                            // only need to track this during odd non zero rulings
                            orientationsSpan[position] = precaclulateSpan[i].RelativeOrientation;
                            buffer[position]           = point;
                            position++;
                            count--;
                        }

                        lastPoint = point;
                    }
                    else
                    {
                        lastPoint = MaxVector;
                    }
                }

                Vector2      startVector = start;
                Span <float> distances   = stackalloc float[position];
                for (int i = 0; i < position; i++)
                {
                    distances[i] = Vector2.DistanceSquared(startVector, buffer[i]);
                }

                QuickSort.Sort(distances, buffer.Slice(0, position), orientationsSpan.Slice(0, position));

                // intersection rules only really apply to closed paths
                if (intersectionRule == IntersectionRule.Nonzero && this.closedPath)
                {
                    int newpositions = 0;
                    int tracker      = 0;
                    for (int i = 0; i < position; i++)
                    {
                        bool include = tracker == 0;
                        switch (orientationsSpan[i])
                        {
                        case Orientation.Clockwise:
                            tracker++;
                            break;

                        case Orientation.Counterclockwise:
                            tracker--;
                            break;

                        case Orientation.Colinear:
                        default:
                            break;
                        }

                        if (include || tracker == 0)
                        {
                            buffer[newpositions] = buffer[i];
                            newpositions++;
                        }
                    }

                    position = newpositions;
                }

                return(position);
            }