예제 #1
0
        /// <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);
            }
        }
예제 #3
0
            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);
            }
예제 #4
0
        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;
        }
예제 #5
0
        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);
        }
예제 #6
0
        /// <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);
        }
예제 #8
0
        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);
        }
예제 #9
0
        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);
        }
예제 #10
0
 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);
 }
예제 #13
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);
            }
        }
예제 #14
0
 /// <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);
예제 #15
0
        /// <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);
        }
예제 #16
0
        /// <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));
        }
예제 #17
0
 /// <inheritdoc/>
 int IPath.FindIntersections(PointF start, PointF end, Span <PointF> buffer, IntersectionRule intersectionRule)
 {
     return(this.innerPath.FindIntersections(start, end, buffer, intersectionRule));
 }
예제 #18
0
 /// <inheritdoc />
 public int FindIntersections(PointF start, PointF end, PointF[] buffer, int offset, IntersectionRule intersectionRule)
 {
     return this.InnerPath.FindIntersections(start, end, buffer.AsSpan(offset), intersectionRule);
 }
예제 #19
0
 /// <inheritdoc />
 public int FindIntersections(PointF start, PointF end, Span<PointF> buffer, IntersectionRule intersectionRule)
 {
     return this.InnerPath.FindIntersections(start, end, buffer, intersectionRule);
 }
예제 #20
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);
            }