/// <inheritdoc /> public int FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule) { int offset = 0; int discovered = 0; Vector2 startPoint = Vector2.Clamp(start, this.topLeft, this.bottomRight); Vector2 endPoint = Vector2.Clamp(end, this.topLeft, this.bottomRight); // start doesn't change when its inside the shape thus not crossing if (startPoint != (Vector2)start) { if (startPoint == Vector2.Clamp(startPoint, start, end)) { // if start closest is within line then its a valid point discovered++; buffer[offset++] = startPoint; } } // end didn't change it must not intercept with an edge if (endPoint != (Vector2)end) { if (endPoint == Vector2.Clamp(endPoint, start, end)) { // if start closest is within line then its a valid point discovered++; buffer[offset] = endPoint; } } return(discovered); }
/// <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) { PointOrientation[] orientations = ArrayPool <PointOrientation> .Shared.Rent(buffer.Length); try { Span <PointOrientation> orientationsSpan = orientations.AsSpan(0, buffer.Length); var position = this.FindIntersectionsWithOrientation(start, end, buffer, orientationsSpan); var activeBuffer = buffer.Slice(0, position); var activeOrientationsSpan = orientationsSpan.Slice(0, position); // intersection rules only really apply to closed paths if (intersectionRule == IntersectionRule.Nonzero && this.closedPath) { position = ApplyNonZeroIntersectionRules(activeBuffer, activeOrientationsSpan); } return(position); } finally { ArrayPool <PointOrientation> .Shared.Return(orientations); } }
public int FindIntersections(PointF s, PointF e, Span <PointF> buffer, IntersectionRule intersectionRule) { Assert.Equal(this.TestYToScan, s.Y); Assert.Equal(this.TestYToScan, e.Y); Assert.True(s.X < this.Bounds.Left); Assert.True(e.X > this.Bounds.Right); this.TestFindIntersectionsInvocationCounter++; return(this.TestFindIntersectionsResult); }
private PolygonScanner( ScanEdgeCollection edgeCollection, int maxIntersectionCount, int minY, int maxY, int subsampling, IntersectionRule intersectionRule, MemoryAllocator allocator) { this.minY = minY; this.maxY = maxY; this.SubpixelDistance = 1f / subsampling; this.SubpixelArea = this.SubpixelDistance / subsampling; this.intersectionRule = intersectionRule; this.edgeCollection = edgeCollection; this.edges = edgeCollection.Edges; int edgeCount = this.edges.Length; int dataBufferSize = (edgeCount * 3) + maxIntersectionCount; // In case of IntersectionRule.Nonzero, we need more space for intersectionTypes: if (intersectionRule == IntersectionRule.Nonzero) { dataBufferSize += maxIntersectionCount; } this.dataBuffer = allocator.Allocate <int>(dataBufferSize); Span <int> dataBufferInt32Span = this.dataBuffer.Memory.Span; Span <float> dataBufferFloatSpan = MemoryMarshal.Cast <int, float>(dataBufferInt32Span); this.sorted0 = dataBufferInt32Span.Slice(0, edgeCount); this.sorted1 = dataBufferInt32Span.Slice(edgeCount, edgeCount); this.activeEdges = new ActiveEdgeList(dataBufferInt32Span.Slice(edgeCount * 2, edgeCount)); this.intersections = dataBufferFloatSpan.Slice(edgeCount * 3, maxIntersectionCount); if (intersectionRule == IntersectionRule.Nonzero) { Span <int> remainder = dataBufferInt32Span.Slice((edgeCount * 3) + maxIntersectionCount, maxIntersectionCount); this.intersectionTypes = MemoryMarshal.Cast <int, NonZeroIntersectionType>(remainder); } else { this.intersectionTypes = default; } this.idx0 = 0; this.idx1 = 0; this.PixelLineY = minY - 1; this.SubPixelY = default; this.yPlusOne = default; }
public static PolygonScanner Create( IPath polygon, int minY, int maxY, int subsampling, IntersectionRule intersectionRule, MemoryAllocator allocator) { var multipolygon = TessellatedMultipolygon.Create(polygon, allocator); var edges = ScanEdgeCollection.Create(multipolygon, allocator, subsampling); var scanner = new PolygonScanner(edges, multipolygon.TotalVertexCount * 2, minY, maxY, subsampling, intersectionRule, allocator); scanner.Init(); return(scanner); }
/// <inheritdoc /> public int FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule) { this.EnsureInternalPathsInitalized(); int totalAdded = 0; InternalPath.PointOrientation[] orientations = ArrayPool <InternalPath.PointOrientation> .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 <InternalPath.PointOrientation> orientationsSpan = orientations; try { foreach (var ip in this.internalPaths) { Span <PointF> subBuffer = buffer.Slice(totalAdded); Span <InternalPath.PointOrientation> 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); SortUtility.Sort(distances, activeBuffer, activeOrientationsSpan); if (intersectionRule == IntersectionRule.Nonzero) { totalAdded = InternalPath.ApplyNonZeroIntersectionRules(activeBuffer, activeOrientationsSpan); } } finally { ArrayPool <InternalPath.PointOrientation> .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); }
public void Fill_EllipsePolygon <TPixel>(TestImageProvider <TPixel> provider, bool reverse, IntersectionRule intersectionRule) where TPixel : unmanaged, IPixel <TPixel> { IPath polygon = new EllipsePolygon(100, 100, 80, 120); if (reverse) { polygon = polygon.Reverse(); } Color color = Color.Azure; provider.RunValidatingProcessorTest( c => { c.SetShapeOptions(new ShapeOptions() { IntersectionRule = intersectionRule }); c.Fill(color, polygon); }, testOutputDetails: $"Reverse({reverse})_IntersectionRule({intersectionRule})", appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); }
public void FillPolygon_Complex <TPixel>(TestImageProvider <TPixel> provider, bool reverse, IntersectionRule intersectionRule) where TPixel : unmanaged, IPixel <TPixel> { PointF[] contour = PolygonFactory.CreatePointArray((20, 20), (80, 20), (80, 80), (20, 80)); PointF[] hole = PolygonFactory.CreatePointArray((40, 40), (40, 60), (60, 60), (60, 40)); if (reverse) { Array.Reverse(contour); Array.Reverse(hole); } var polygon = new ComplexPolygon( new Path(new LinearLineSegment(contour)), new Path(new LinearLineSegment(hole))); provider.RunValidatingProcessorTest( c => { c.SetShapeOptions(new ShapeOptions() { IntersectionRule = intersectionRule }); c.Fill(Color.White, polygon); }, testOutputDetails: $"Reverse({reverse})_IntersectionRule({intersectionRule})", comparer: ImageComparer.TolerantPercentage(0.01f), appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); }
public int FindIntersections(PointF start, PointF end, PointF[] buffer, int offset, IntersectionRule intersectionRule) { return(this.FindIntersections(start, end, buffer, 0)); }
public override int Scan(float y, Span <float> buffer, Configuration configuration, IntersectionRule intersectionRule) { this.ScanInvocationCounter++; return(0); }
public override int Scan(float y, Span <float> buffer, Configuration configuration, IntersectionRule intersectionRule) { if (y < 5) { buffer[0] = -10f; buffer[1] = 100f; return(2); } return(0); }
/// <inheritdoc/> public override int Scan(float y, Span <float> buffer, Configuration configuration, IntersectionRule intersectionRule) { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); using (IMemoryOwner <PointF> tempBuffer = configuration.MemoryAllocator.Allocate <PointF>(buffer.Length)) { Span <PointF> innerBuffer = tempBuffer.Memory.Span; int count = this.Shape.FindIntersections(start, end, innerBuffer, intersectionRule); for (int i = 0; i < count; i++) { buffer[i] = innerBuffer[i].X; } return(count); } }
/// <summary> /// Scans the X axis for intersections at the Y axis position. /// </summary> /// <param name="y">The position along the y axis to find intersections.</param> /// <param name="buffer">The buffer.</param> /// <param name="configuration">A <see cref="Configuration"/> instance in the context of the caller.</param> /// <param name="intersectionRule">How intersections are handled.</param> /// <returns>The number of intersections found.</returns> public abstract int Scan(float y, Span <float> buffer, Configuration configuration, IntersectionRule intersectionRule);
/// <summary> /// Finds the intersections. /// </summary> /// <param name="start">The start.</param> /// <param name="end">The end.</param> /// <returns>The points along the line the intersect with the boundaries of the polygon.</returns> internal static IEnumerable <PointF> FindIntersections(this InternalPath path, Vector2 start, Vector2 end, IntersectionRule intersectionRule = IntersectionRule.OddEven) { var results = new List <PointF>(); PointF[] buffer = ArrayPool <PointF> .Shared.Rent(path.PointCount); try { int hits = path.FindIntersections(start, end, buffer, intersectionRule); for (int i = 0; i < hits; i++) { results.Add(buffer[i]); } } finally { ArrayPool <PointF> .Shared.Return(buffer); } return(results); }
/// <inheritdoc/> int IPath.FindIntersections(PointF start, PointF end, PointF[] buffer, int offset, IntersectionRule intersectionRule) { Span <PointF> subBuffer = buffer.AsSpan(offset); return(this.innerPath.FindIntersections(start, end, subBuffer, intersectionRule)); }
/// <inheritdoc/> int IPath.FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule) { return(this.innerPath.FindIntersections(start, end, buffer, intersectionRule)); }
/// <inheritdoc /> public int FindIntersections(PointF start, PointF end, PointF[] buffer, int offset, IntersectionRule intersectionRule) { return this.InnerPath.FindIntersections(start, end, buffer.AsSpan(offset), intersectionRule); }
/// <inheritdoc /> public int FindIntersections(PointF start, PointF end, Span<PointF> buffer, IntersectionRule intersectionRule) { return this.InnerPath.FindIntersections(start, end, buffer, intersectionRule); }
/// <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); }