public void ClippedTriangleGapInIntersections() { Polygon simplePath = new Polygon(new LinearLineSegment( new PointF(10, 10), new PointF(200, 150), new PointF(50, 300))); Polygon hole1 = new Polygon(new LinearLineSegment( new PointF(37, 85), new PointF(93, 85), new PointF(65, 137))); IPath clippedPath = simplePath.Clip(hole1); IPath outline = clippedPath.GenerateOutline(5, new[] { 1f }); PointF[] buffer = new PointF[20]; PointF start = new PointF(outline.Bounds.Left - 1, 102); PointF end = new PointF(outline.Bounds.Right + 1, 102); int matches = outline.FindIntersections(start, end, buffer, 0); int maxIndex = buffer.Select((x, i) => new { x, i }).Where(x => x.x.X > 0).Select(x => x.i).Last(); Assert.Equal(matches - 1, maxIndex); }
public void SmallRingAtLargeCoords_HorizontalScansShouldFind4IntersectionPoints(int w, int h, int r, int thickness) { int cx = w - 2 * r; int cy = h - 2 * 3; EllipsePolygon ellipse = new EllipsePolygon(cx, cy, r); IPath path = ellipse.GenerateOutline(thickness); int yMin = cy - r + thickness + 1; int yMax = cy + r - thickness; PointF[] buffer = new PointF[16]; List <int> badPositions = new List <int>(); for (int y = yMin; y < yMax; y++) { PointF start = new PointF(-1, y); PointF end = new PointF(w + 1, y); int intersectionCount = path.FindIntersections(start, end, buffer); if (intersectionCount != 4) { badPositions.Add(y); } } if (badPositions.Any()) { string badPoz = string.Join(',', badPositions); this.Output.WriteLine($"BAD: {badPositions.Count} of {yMax - yMin}: {badPoz}"); Assert.True(false); } }
public int ScanY(IPath shape, int y, float[] buffer, int length, int offset) { PointF start = new PointF(shape.Bounds.Left - 1, y); PointF end = new PointF(shape.Bounds.Right + 1, y); PointF[] innerbuffer = ArrayPool <PointF> .Shared.Rent(length); Span <PointF> span = new Span <PointF>(innerbuffer); try { int count = shape.FindIntersections(start, end, span); for (int i = 0; i < count; i++) { buffer[i + offset] = innerbuffer[i].X; } return(count); } finally { ArrayPool <PointF> .Shared.Return(innerbuffer); } }
public void SpecificMisses(string name, int yScanLine) { IPath polygon = ShapesData[name]; int intersections = polygon.FindIntersections(new Vector2(polygon.Bounds.Left - 1, yScanLine), new Vector2(polygon.Bounds.Right + 1, yScanLine)).Count(); Assert.True(intersections % 2 == 0, $"crosssection of '{name}' at '{yScanLine}' produced {intersections} intersections"); }
public void ShapeMissingEdgeHits(string name) { IPath polygon = ShapesData[name]; int top = (int)Math.Ceiling(polygon.Bounds.Top); int bottom = (int)Math.Floor(polygon.Bounds.Bottom); for (int y = top; y <= bottom; y++) { IEnumerable <PointF> intersections = polygon.FindIntersections(new Vector2(polygon.Bounds.Left - 1, y), new Vector2(polygon.Bounds.Right + 1, y)); if (intersections.Count() % 2 != 0) { Assert.True(false, $"crosssection of '{name}' at '{y}' produced {intersections.Count()} number of intersections"); } } }
/// <summary> /// Finds the intersections. /// </summary> /// <param name="path">The path.</param> /// <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> public static IEnumerable <Vector2> FindIntersections(this IPath path, Vector2 start, Vector2 end) { var buffer = ArrayPool <Vector2> .Shared.Rent(path.MaxIntersections); try { var hits = path.FindIntersections(start, end, buffer, path.MaxIntersections, 0); var results = new Vector2[hits]; for (var i = 0; i < hits; i++) { results[i] = buffer[i]; } return(results); } finally { ArrayPool <Vector2> .Shared.Return(buffer); } }
/// <summary> /// Finds the intersections. /// </summary> /// <param name="path">The path.</param> /// <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> public static IEnumerable <PointF> FindIntersections(this IPath path, PointF start, PointF end) { PointF[] buffer = ArrayPool <PointF> .Shared.Rent(path.MaxIntersections); try { int hits = path.FindIntersections(start, end, buffer, 0); PointF[] results = new PointF[hits]; for (int i = 0; i < hits; i++) { results[i] = buffer[i]; } return(results); } finally { ArrayPool <PointF> .Shared.Return(buffer); } }
public void SmallRingAtLargeCoords_HorizontalScansShouldFind4IntersectionPoints(int w, int h, int r, int thickness) { int cx = w - (2 * r); int cy = h - (2 * 3); var ellipse = new EllipsePolygon(cx, cy, r); IPath path = ellipse.GenerateOutline(thickness); int yMin = cy - r + thickness + 1; int yMax = cy + r - thickness; var buffer = new PointF[16]; var badPositions = new List <int>(); for (int y = yMin; y < yMax; y++) { var start = new PointF(-1, y); var end = new PointF(w + 1, y); int intersectionCount = path.FindIntersections(start, end, buffer); if (intersectionCount != 4) { badPositions.Add(y); } } if (badPositions.Count > 0) { // The char overload is causing mysterious build failures in Visual Studio string badPoz = string.Join(",", badPositions); this.Output.WriteLine($"BAD: {badPositions.Count} of {yMax - yMin}: {badPoz}"); Assert.True(false); } }
private Buffer2D <float> Render(IPath path) { Size size = Rectangle.Ceiling(path.Bounds).Size; size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2)); float subpixelCount = 4; float offset = 0.5f; if (this.Options.Antialias) { offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. subpixelCount = this.Options.AntialiasSubpixelDepth; if (subpixelCount < 4) { subpixelCount = 4; } } // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it. Buffer2D <float> fullBuffer = this.MemoryAllocator.Allocate2D <float>(size.Width + 1, size.Height + 1, true); using (IBuffer <float> bufferBacking = this.MemoryAllocator.Allocate <float>(path.MaxIntersections)) using (IBuffer <PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate <PointF>(size.Width)) { float subpixelFraction = 1f / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount; for (int y = 0; y <= size.Height; y++) { Span <float> scanline = fullBuffer.GetRowSpan(y); bool scanlineDirty = false; float yPlusOne = y + 1; for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction) { var start = new PointF(path.Bounds.Left - 1, subPixel); var end = new PointF(path.Bounds.Right + 1, subPixel); Span <PointF> intersectionSpan = rowIntersectionBuffer.GetSpan(); Span <float> buffer = bufferBacking.GetSpan(); int pointsFound = path.FindIntersections(start, end, intersectionSpan); if (pointsFound == 0) { // nothing on this line skip continue; } for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++) { buffer[i] = intersectionSpan[i].X; } QuickSort.Sort(buffer.Slice(0, pointsFound)); for (int point = 0; point < pointsFound; point += 2) { // points will be paired up float scanStart = buffer[point]; float scanEnd = buffer[point + 1]; int startX = (int)MathF.Floor(scanStart + offset); int endX = (int)MathF.Floor(scanEnd + offset); if (startX >= 0 && startX < scanline.Length) { for (float x = scanStart; x < startX + 1; x += subpixelFraction) { scanline[startX] += subpixelFractionPoint; scanlineDirty = true; } } if (endX >= 0 && endX < scanline.Length) { for (float x = endX; x < scanEnd; x += subpixelFraction) { scanline[endX] += subpixelFractionPoint; scanlineDirty = true; } } int nextX = startX + 1; endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge nextX = Math.Max(nextX, 0); for (int x = nextX; x < endX; x++) { scanline[x] += subpixelFraction; scanlineDirty = true; } } } if (scanlineDirty) { if (!this.Options.Antialias) { for (int x = 0; x < size.Width; x++) { if (scanline[x] >= 0.5) { scanline[x] = 1; } else { scanline[x] = 0; } } } } } } return(fullBuffer); }