/// <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); }
/// <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); }