Ejemplo n.º 1
0
        // end ScanIntersect()

        #endregion // Scanline utilities

        #region IComparer<SegEvent>

        /// <summary>
        /// For ordering events first by Y, then X, then by whether it's an H or V seg.
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public int Compare(SegEvent first, SegEvent second)
        {
            if (first == second)
            {
                return(0);
            }

            if (first == null)
            {
                return(-1);
            }

            if (second == null)
            {
                return(1);
            }

            // Unlike the ScanSegment-generating scanline in VisibilityGraphGenerator, this scanline has no slope
            // calculations so no additional rounding error is introduced.
            int cmp = PointComparer.Compare(first.Site.Y, second.Site.Y);

            if (0 != cmp)
            {
                return(cmp);
            }

            // Both are at same Y so we must ensure that for equivalent Y, VClose comes after
            // HOpen which comes after VOpen, thus make sure VOpen comes before VClose.
            if (first.IsVertical && second.IsVertical)
            {
                // Separate segments may join at Start and End due to overlap.
                Debug.Assert(!StaticGraphUtility.IntervalsOverlap(first.Segment, second.Segment) ||
                             (0 == PointComparer.Compare(first.Segment.Start, second.Segment.End)) ||
                             (0 == PointComparer.Compare(first.Segment.End, second.Segment.Start))
                             , "V subsumption failure detected in SegEvent comparison");
                if (0 == cmp)
                {
                    // false is < true.
                    cmp = (SegEventType.VClose == first.EventType).CompareTo(SegEventType.VClose == second.EventType);
                }
                return(cmp);
            }

            // If both are H segs, then sub-order by X.
            if (!first.IsVertical && !second.IsVertical)
            {
                // Separate segments may join at Start and End due to overlap, so compare by Start.X;
                // the ending segment (lowest Start.X) comes before the Open (higher Start.X).
                Debug.Assert(!StaticGraphUtility.IntervalsOverlap(first.Segment, second.Segment) ||
                             (0 == PointComparer.Compare(first.Segment.Start, second.Segment.End)) ||
                             (0 == PointComparer.Compare(first.Segment.End, second.Segment.Start))
                             , "H subsumption failure detected in SegEvent comparison");
                cmp = PointComparer.Compare(first.Site.X, second.Site.X);
                return(cmp);
            }

            // One is Vertical and one is Horizontal; we are only interested in the vertical at this point.
            SegEvent vEvent = first.IsVertical ? first : second;

            // Make sure that we have opened all V segs before and closed them after opening
            // an H seg at the same Y coord. Otherwise we'll miss "T" or "corner" intersections.
            // (RectilinearTests.Connected_Vertical_Segments_Are_Intersected tests that we get the expected count here.)
            // Start assuming Vevent is 'first' and it's VOpen, which should come before HOpen.
            cmp = -1; // Start with first == VOpen
            if (SegEventType.VClose == vEvent.EventType)
            {
                cmp = 1; // change to first == VClose
            }
            if (vEvent != first)
            {
                cmp *= -1; // undo the swap.
            }
            return(cmp);
        }
Ejemplo n.º 2
0
        // If we have collinear segments, then we may be able to just update the previous one
        // instead of growing the ScanSegmentTree.
        //  - For multiple collinear OpenVertexEvents, neighbors to the high side have not yet
        //    been seen, so a segment is created that spans the lowest and highest neighbors.
        //    A subsequent collinear OpenVertexEvent will be to the high side and will add a
        //    subsegment of that segment, so we subsume it into LastAddedSegment.
        //  - For multiple collinear CloseVertexEvents, closing neighbors to the high side are
        //    still open, so a segment is created from the lowest neighbor to the next-highest
        //    collinear obstacle to be closed.  When that next-highest CloseVertexEvent is
        //    encountered, it will extend LastAddedSegment.
        //  - For multiple collinear mixed Open and Close events, we'll do all Opens first,
        //    followed by all closes (per EventQueue opening), so we may add multiple discrete
        //    segments, which ScanSegmentTree will merge.
        internal static bool Subsume(ref ScanSegment seg, Point newStart, Point newEnd,
                                     double weight, PointAndCrossingsList gbcList,
                                     ScanDirection scanDir, ScanSegmentTree tree, out bool extendStart,
                                     out bool extendEnd)
        {
            // Initialize these to the non-subsumed state; the endpoints were extended (or on a
            // different line).
            extendStart = true;
            extendEnd   = true;
            if (null == seg)
            {
                return(false);
            }

            // If they don't overlap (including touching at an endpoint), we don't subsume.
            if (!StaticGraphUtility.IntervalsOverlap(seg.Start, seg.End, newStart, newEnd))
            {
                return(false);
            }

            // If the overlapped-ness isn't the same, we don't subsume.  ScanSegmentTree::MergeSegments
            // will mark that the low-to-high direction needs a VisibilityVertex to link the two segments.
            // These may differ by more than Curve.DistanceEpsilon in the case of reflection lookahead
            // segments collinear with vertex-derived segments, so have a looser tolerance here and we'll
            // adjust the segments in ScanSegmentTree.MergeSegments.
            if (seg.Weight != weight)
            {
                if ((seg.Start == newStart) && (seg.End == newEnd))
                {
                    // This is probably because of a rounding difference by one DistanceEpsilon reporting being
                    // inside an obstacle vs. the scanline intersection calculation side-ordering.
                    // Test is RectilinearFileTests.Overlap_Rounding_Vertex_Intersects_Side.
                    seg.Weight = Math.Min(seg.Weight, weight);
                    return(true);
                }

                // In the case of groups, we go through the group boundary; this may coincide with a
                // reflection segment. RectilinearFileTests.ReflectionSubsumedBySegmentExitingGroup.
                Debug.Assert((seg.Weight == OverlappedWeight) == (weight == OverlappedWeight) ||
                             ApproximateComparer.CloseIntersections(seg.End, newStart) ||
                             ApproximateComparer.CloseIntersections(seg.Start, newEnd)
                             , "non-equal overlap-mismatched ScanSegments overlap by more than just Start/End");
                return(false);
            }

            // Subsume the input segment.  Return whether the start/end points were extended (newStart
            // is before this.Start, or newEnd is after this.End), so the caller can generate reflections
            // and so we can merge group border crossings.
            extendStart = (-1 == scanDir.CompareScanCoord(newStart, seg.Start));
            extendEnd   = (1 == scanDir.CompareScanCoord(newEnd, seg.End));
            if (extendStart || extendEnd)
            {
                // We order by start and end so need to replace this in the tree regardless of which end changes.
                tree.Remove(seg);
                seg.startPoint = scanDir.Min(seg.Start, newStart);
                seg.endPoint   = scanDir.Max(seg.End, newEnd);
                seg            = tree.InsertUnique(seg).Item;
                seg.MergeGroupBoundaryCrossingList(gbcList);
            }
            return(true);
        }