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