/// <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>
        /// <returns>number of intersections hit</returns>
        public int FindIntersections(PointF start, PointF end, Span <PointF> buffer)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(nameof(InternalPath));
            }

            if (_points.Count < 2)
            {
                return(0);
            }

            int count = buffer.Length;

            ClampPoints(ref start, ref end);

            var target = new Segment(start, end);

            int polyCorners = _points.Count;

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

            int     position  = 0;
            Vector2 lastPoint = MaxVector;

            PassPointData[] precaclulate = ArrayPool <PassPointData> .Shared.Rent(_points.Count);

            try
            {
                // pre calculate relative orientations X places ahead and behind
                Vector2     startToEnd           = end - start;
                Orientation prevOrientation      = CalulateOrientation(startToEnd, _points[polyCorners - 1].Point - end);
                Orientation nextOrientation      = CalulateOrientation(startToEnd, _points[0].Point - end);
                Orientation nextPlus1Orientation = CalulateOrientation(startToEnd, _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++)
                {
                    Segment edge = _points[i].Segment;

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

                    // should this point cause the last matched point to be excluded
                    bool removeLastIntersection = nextOrientation == Orientation.Colinear &&
                                                  pointOrientation == Orientation.Colinear &&
                                                  nextPlus1Orientation != prevOrientation &&
                                                  (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;
                    }

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

                    prevOrientation = pointOrientation;
                }

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

                    if (precaclulate[prev].DoIntersect)
                    {
                        lastPoint = FindIntersection(_points[prev].Segment, target);
                    }
                }

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

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

                        continue;
                    }

                    if (precaclulate[i].DoIntersect)
                    {
                        Vector2 point = FindIntersection(_points[i].Segment, target);
                        if (point != MaxVector)
                        {
                            if (lastPoint.Equivalent(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 (_points[next].Point.Equivalent(point, Epsilon))
                                {
                                    next = i;
                                }

                                if (_points[last].Point.Equivalent(point, Epsilon))
                                {
                                    last = i;
                                }

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

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

                            // we are not double crossing so just add it once
                            buffer[position] = point;
                            position++;
                            count--;
                        }

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

                return(position);
            }
            finally
            {
                ArrayPool <PassPointData> .Shared.Return(precaclulate);
            }
        }
        /// <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="orientationsSpan">The buffer for storeing the orientation of each intersection.</param>
        /// <returns>number of intersections hit</returns>
        public int FindIntersectionsWithOrientation(PointF start, PointF end, Span <PointF> buffer, Span <PointOrientation> orientationsSpan)
        {
            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);

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

            try
            {
                // pre calculate relative orientations X places ahead and behind
                Vector2          startToEnd           = end - start;
                PointOrientation prevOrientation      = CalulateOrientation(startToEnd, this.points[polyCorners - 1].Point - end);
                PointOrientation nextOrientation      = CalulateOrientation(startToEnd, this.points[0].Point - end);
                PointOrientation 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
                    PointOrientation 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 == PointOrientation.Colinear &&
                                                  pointOrientation == PointOrientation.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 == PointOrientation.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.Equivalent(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.Equivalent(point, Epsilon))
                                {
                                    next = i;
                                }

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

                                PointOrientation side  = precaclulateSpan[next].RelativeOrientation;
                                PointOrientation 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]);
                }

                var activeBuffer           = buffer.Slice(0, position);
                var activeOrientationsSpan = orientationsSpan.Slice(0, position);
                SortUtility.Sort(distances, activeBuffer, activeOrientationsSpan);

                return(position);
            }