internal PointAndCrossingsList GetOrderedListBetween(Point start, Point end) { if (0 == pointCrossingMap.Count) { return null; } if (PointComparer.Compare(start, end) > 0) { Point temp = start; start = end; end = temp; } // Start and end are inclusive. pointList.Clear(); foreach (var intersection in pointCrossingMap.Keys) { if ((PointComparer.Compare(intersection, start) >= 0) && (PointComparer.Compare(intersection, end) <= 0)) { pointList.Add(intersection); } } pointList.Sort(); var pointAndCrossingList = new PointAndCrossingsList(); var numCrossings = pointList.Count; for (int ii = 0; ii < numCrossings; ++ii) { Point intersect = pointList[ii]; pointAndCrossingList.Add(intersect, pointCrossingMap[intersect]); } return pointAndCrossingList; }
internal void MergeFrom(PointAndCrossingsList other) { Reset(); if ((null == other) || (0 == other.ListOfPointsAndCrossings.Count)) { return; } if (0 == this.ListOfPointsAndCrossings.Count) { this.ListOfPointsAndCrossings.AddRange(other.ListOfPointsAndCrossings); } if (null == this.ListOfPointsAndCrossings) { this.ListOfPointsAndCrossings = new List <PointAndCrossings>(other.ListOfPointsAndCrossings); return; } // Do the usual sorted-list merge. int thisIndex = 0, thisMax = this.ListOfPointsAndCrossings.Count; int otherIndex = 0, otherMax = other.ListOfPointsAndCrossings.Count; var newCrossingsList = new List <PointAndCrossings>(this.ListOfPointsAndCrossings.Count); while ((thisIndex < thisMax) || (otherIndex < otherMax)) { if (thisIndex >= thisMax) { newCrossingsList.Add(other.ListOfPointsAndCrossings[otherIndex++]); continue; } if (otherIndex >= otherMax) { newCrossingsList.Add(this.ListOfPointsAndCrossings[thisIndex++]); continue; } PointAndCrossings thisPac = this.ListOfPointsAndCrossings[thisIndex]; PointAndCrossings otherPac = other.ListOfPointsAndCrossings[otherIndex]; int cmp = PointComparer.Compare(thisPac.Location, otherPac.Location); if (0 == cmp) { // No duplicates newCrossingsList.Add(thisPac); ++thisIndex; ++otherIndex; } else if (-1 == cmp) { newCrossingsList.Add(thisPac); ++thisIndex; } else { newCrossingsList.Add(otherPac); ++otherIndex; } } this.ListOfPointsAndCrossings = newCrossingsList; }
internal PointAndCrossingsList GetOrderedListBetween(Point start, Point end) { if (0 == pointCrossingMap.Count) { return(null); } if (PointComparer.Compare(start, end) > 0) { Point temp = start; start = end; end = temp; } // Start and end are inclusive. pointList.Clear(); foreach (var intersection in pointCrossingMap.Keys) { if ((PointComparer.Compare(intersection, start) >= 0) && (PointComparer.Compare(intersection, end) <= 0)) { pointList.Add(intersection); } } pointList.Sort(); var pointAndCrossingList = new PointAndCrossingsList(); var numCrossings = pointList.Count; for (int ii = 0; ii < numCrossings; ++ii) { Point intersect = pointList[ii]; pointAndCrossingList.Add(intersect, pointCrossingMap[intersect]); } return(pointAndCrossingList); }
// For group boundary crossings. internal void MergeGroupBoundaryCrossingList(PointAndCrossingsList other) { if (null != other) { if (null == GroupBoundaryPointAndCrossingsList) { GroupBoundaryPointAndCrossingsList = new PointAndCrossingsList(); } GroupBoundaryPointAndCrossingsList.MergeFrom(other); } }
internal void OnSegmentIntersectorEnd(VisibilityGraph vg) { AppendGroupCrossingsThroughPoint(vg, End); GroupBoundaryPointAndCrossingsList = null; if ((null == HighestVisibilityVertex) || (PointComparer.IsPureLower(HighestVisibilityVertex.Point, End))) { LoadEndOverlapVertexIfNeeded(vg); } }
private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment) { if ((null == crossingList) || (0 == crossingList.Count)) { return; } crossingList.Reset(); var start = maxSegment.Start; var end = maxSegment.End; var dir = PointComparer.GetPureDirection(start, end); // Make sure we are going in the ascending direction. if (!StaticGraphUtility.IsAscending(dir)) { start = maxSegment.End; end = maxSegment.Start; dir = CompassVector.OppositeDir(dir); } // We need to back up to handle group crossings that are between a VisibilityBorderIntersect on a sloped border and the // incoming startVertex (which is on the first ScanSegment in Perpendicular(dir) that is outside that padded border). startVertex = TraverseToFirstVertexAtOrAbove(startVertex, start, CompassVector.OppositeDir(dir)); // Splice into the Vertices between and including the start/end points. for (var currentVertex = startVertex; null != currentVertex; currentVertex = StaticGraphUtility.FindNextVertex(currentVertex, dir)) { bool isFinalVertex = (PointComparer.Compare(currentVertex.Point, end) >= 0); while (crossingList.CurrentIsBeforeOrAt(currentVertex.Point)) { PointAndCrossings pac = crossingList.Pop(); // If it's past the start and at or before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) > 0) { if (PointComparer.Compare(pac.Location, end) <= 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, CompassVector.OppositeDir(dir)); } } // If it's at or past the start and before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) >= 0) { if (PointComparer.Compare(pac.Location, end) < 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, dir); } } } if (isFinalVertex) { break; } } }
private bool AppendGroupCrossingsThroughPoint(VisibilityGraph vg, Point lastPoint) { if (null == GroupBoundaryPointAndCrossingsList) { return(false); } bool found = false; while (GroupBoundaryPointAndCrossingsList.CurrentIsBeforeOrAt(lastPoint)) { // We will only create crossing Edges that the segment actually crosses, not those it ends before crossing. // For those terminal crossings, the adjacent segment creates the interior vertex and crossing edge. PointAndCrossings pac = GroupBoundaryPointAndCrossingsList.Pop(); GroupBoundaryCrossing[] lowDirCrossings = null; GroupBoundaryCrossing[] highDirCrossings = null; if (PointComparer.Compare(pac.Location, Start) > 0) { lowDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, ScanDirection.OppositeDirection); } if (PointComparer.Compare(pac.Location, End) < 0) { highDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, ScanDirection.Direction); } found = true; VisibilityVertex crossingVertex = vg.FindVertex(pac.Location) ?? vg.AddVertex(pac.Location); if ((null != lowDirCrossings) || (null != highDirCrossings)) { AddLowCrossings(vg, crossingVertex, lowDirCrossings); AddHighCrossings(vg, crossingVertex, highDirCrossings); } else { // This is at this.Start with only lower-direction toward group interior(s), or at this.End with only // higher-direction toward group interior(s). Therefore an adjacent ScanSegment will create the crossing // edge, so create the crossing vertex here and we'll link to it. if (null == LowestVisibilityVertex) { SetInitialVisibilityVertex(crossingVertex); } else { Debug.Assert(PointComparer.Equal(End, crossingVertex.Point), "Expected this.End crossingVertex"); AppendHighestVisibilityVertex(crossingVertex); } } } return(found); }
private void ExtendEdgeChain(VisibilityVertex startVertex, Directions extendDir , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment , PointAndCrossingsList pacList, bool isOverlapped) { StaticGraphUtility.Assert(PointComparer.GetPureDirection(maxDesiredSegment.Start, maxDesiredSegment.End) == extendDir , "maxDesiredSegment is reversed", ObstacleTree, VisGraph); // Direction*s*, because it may return None, which is valid and means startVertex is on the // border of an obstacle and we don't want to go inside it. Directions segmentDir = PointComparer.GetDirections(startVertex.Point, maxDesiredSegment.End); if (segmentDir != extendDir) { // OppositeDir may happen on overlaps where the boundary has a gap in its ScanSegments due to other obstacles // overlapping it and each other. This works because the port has an edge connected to startVertex, // which is on a ScanSegment outside the obstacle. StaticGraphUtility.Assert(isOverlapped || (segmentDir != CompassVector.OppositeDir(extendDir)) , "obstacle encountered between prevPoint and startVertex", ObstacleTree, VisGraph); return; } // We'll find the segment to the left (or right if to the left doesn't exist), // then splice across in the opposite direction. Directions spliceSourceDir = CompassVector.RotateLeft(extendDir); VisibilityVertex spliceSource = StaticGraphUtility.FindNextVertex(startVertex, spliceSourceDir); if (null == spliceSource) { spliceSourceDir = CompassVector.OppositeDir(spliceSourceDir); spliceSource = StaticGraphUtility.FindNextVertex(startVertex, spliceSourceDir); if (null == spliceSource) { return; } } // Store this off before ExtendSpliceWorker, which overwrites it. Directions spliceTargetDir = CompassVector.OppositeDir(spliceSourceDir); VisibilityVertex spliceTarget; if (ExtendSpliceWorker(spliceSource, extendDir, spliceTargetDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget)) { // We ended on the source side and may have dead-ends on the target side so reverse sides. ExtendSpliceWorker(spliceTarget, extendDir, spliceSourceDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget); } SpliceGroupBoundaryCrossings(pacList, startVertex, maxDesiredSegment); }
internal void MergeFrom(PointAndCrossingsList other) { Reset(); if ((null == other) || (0 == other.ListOfPointsAndCrossings.Count)) { return; } if (0 == this.ListOfPointsAndCrossings.Count) { this.ListOfPointsAndCrossings.AddRange(other.ListOfPointsAndCrossings); } if (null == this.ListOfPointsAndCrossings) { this.ListOfPointsAndCrossings = new List<PointAndCrossings>(other.ListOfPointsAndCrossings); return; } // Do the usual sorted-list merge. int thisIndex = 0, thisMax = this.ListOfPointsAndCrossings.Count; int otherIndex = 0, otherMax = other.ListOfPointsAndCrossings.Count; var newCrossingsList = new List<PointAndCrossings>(this.ListOfPointsAndCrossings.Count); while ((thisIndex < thisMax) || (otherIndex < otherMax)) { if (thisIndex >= thisMax) { newCrossingsList.Add(other.ListOfPointsAndCrossings[otherIndex++]); continue; } if (otherIndex >= otherMax) { newCrossingsList.Add(this.ListOfPointsAndCrossings[thisIndex++]); continue; } PointAndCrossings thisPac = this.ListOfPointsAndCrossings[thisIndex]; PointAndCrossings otherPac = other.ListOfPointsAndCrossings[otherIndex]; int cmp = PointComparer.Compare(thisPac.Location, otherPac.Location); if (0 == cmp) { // No duplicates newCrossingsList.Add(thisPac); ++thisIndex; ++otherIndex; } else if (-1 == cmp) { newCrossingsList.Add(thisPac); ++thisIndex; } else { newCrossingsList.Add(otherPac); ++otherIndex; } } this.ListOfPointsAndCrossings = newCrossingsList; }
internal void ExtendEdgeChain(VisibilityVertex startVertex, Rectangle limitRect, LineSegment maxVisibilitySegment, PointAndCrossingsList pacList, bool isOverlapped) { var dir = PointComparer.GetDirections(maxVisibilitySegment.Start, maxVisibilitySegment.End); if (dir == Directions.None) { return; } Debug.Assert(CompassVector.IsPureDirection(dir), "impure max visibility segment"); // Shoot the edge chain out to the shorter of max visibility or intersection with the limitrect. StaticGraphUtility.Assert(PointComparer.Equal(maxVisibilitySegment.Start, startVertex.Point) || (PointComparer.GetPureDirection(maxVisibilitySegment.Start, startVertex.Point) == dir) , "Inconsistent direction found", ObstacleTree, VisGraph); double oppositeFarBound = StaticGraphUtility.GetRectangleBound(limitRect, dir); Point maxDesiredSplicePoint = StaticGraphUtility.IsVertical(dir) ? ApproximateComparer.Round(new Point(startVertex.Point.X, oppositeFarBound)) : ApproximateComparer.Round(new Point(oppositeFarBound, startVertex.Point.Y)); if (PointComparer.Equal(maxDesiredSplicePoint, startVertex.Point)) { // Nothing to do. return; } if (PointComparer.GetPureDirection(startVertex.Point, maxDesiredSplicePoint) != dir) { // It's in the opposite direction, so no need to do anything. return; } // If maxDesiredSplicePoint is shorter, create a new shorter segment. We have to pass both segments // through to the worker function so it knows whether it can go past maxDesiredSegment (which may be limited // by limitRect). var maxDesiredSegment = maxVisibilitySegment; if (PointComparer.GetDirections(maxDesiredSplicePoint, maxDesiredSegment.End) == dir) { maxDesiredSegment = new LineSegment(maxDesiredSegment.Start, maxDesiredSplicePoint); } ExtendEdgeChain(startVertex, dir, maxDesiredSegment, maxVisibilitySegment, pacList, isOverlapped); }
private void SpliceGroupBoundaryCrossing(VisibilityVertex currentVertex, PointAndCrossings pac, Directions dirToInside) { GroupBoundaryCrossing[] crossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, dirToInside); if (null != crossings) { var outerVertex = VisGraph.FindVertex(pac.Location) ?? AddVertex(pac.Location); if (currentVertex.Point != outerVertex.Point) { FindOrAddEdge(currentVertex, outerVertex); } var interiorPoint = crossings[0].GetInteriorVertexPoint(pac.Location); var interiorVertex = VisGraph.FindVertex(interiorPoint) ?? AddVertex(interiorPoint); // FindOrAddEdge splits an existing edge so may not return the portion bracketed by outerVertex and interiorVertex. FindOrAddEdge(outerVertex, interiorVertex); var edge = VisGraph.FindEdge(outerVertex.Point, interiorVertex.Point); var crossingsArray = crossings.Select(c => c.Group.InputShape).ToArray(); edge.IsPassable = delegate { return(crossingsArray.Any(s => s.IsTransparent)); }; } }
internal void CreateSparseVerticesAndEdges(VisibilityGraph vg) { if (this.sparsePerpendicularCoords == null) { return; } AppendGroupCrossingsThroughPoint(vg, Start); foreach (var perpCoord in this.sparsePerpendicularCoords.OrderBy(d => d)) { var vertexLocation = this.CreatePointFromPerpCoord(perpCoord); Debug.Assert(this.ContainsPoint(vertexLocation), "vertexLocation is not on Segment"); this.AppendVisibilityVertex(vg, vg.FindVertex(vertexLocation) ?? vg.AddVertex(vertexLocation)); } AppendGroupCrossingsThroughPoint(vg, End); GroupBoundaryPointAndCrossingsList = null; this.sparsePerpendicularCoords.Clear(); this.sparsePerpendicularCoords = null; }
/// <summary> /// Appends a ScanSegment to the linked list in the "Current" slot. /// </summary> internal void CreateScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList) { this.CurrentSlot.AppendScanSegment(new ScanSegment(start, end, weight, gbcList)); }
/// <summary> /// Create a LineSegment that contains the max visibility from startPoint in the desired direction. /// </summary> internal LineSegment CreateMaxVisibilitySegment(Point startPoint, Directions dir, out PointAndCrossingsList pacList) { var graphBoxBorderIntersect = StaticGraphUtility.RectangleBorderIntersect(this.GraphBox, startPoint, dir); if (PointComparer.GetDirections(startPoint, graphBoxBorderIntersect) == Directions. None) { pacList = null; return new LineSegment(startPoint, startPoint); } var segment = this.RestrictSegmentWithObstacles(startPoint, graphBoxBorderIntersect); // Store this off before other operations which overwrite it. pacList = this.CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(segment.Start, segment.End); return segment; }
internal ScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList) { Update(start, end); Weight = weight; GroupBoundaryPointAndCrossingsList = gbcList; }
internal void ExtendEdgeChain(VisibilityVertex startVertex, Rectangle limitRect, LineSegment maxVisibilitySegment, PointAndCrossingsList pacList, bool isOverlapped) { var dir = PointComparer.GetDirections(maxVisibilitySegment.Start, maxVisibilitySegment.End); if (dir == Directions. None) { return; } Debug.Assert(CompassVector.IsPureDirection(dir), "impure max visibility segment"); // Shoot the edge chain out to the shorter of max visibility or intersection with the limitrect. StaticGraphUtility.Assert(PointComparer.Equal(maxVisibilitySegment.Start, startVertex.Point) || (PointComparer.GetPureDirection(maxVisibilitySegment.Start, startVertex.Point) == dir) , "Inconsistent direction found", ObstacleTree, VisGraph); double oppositeFarBound = StaticGraphUtility.GetRectangleBound(limitRect, dir); Point maxDesiredSplicePoint = StaticGraphUtility.IsVertical(dir) ? ApproximateComparer.Round(new Point(startVertex.Point.X, oppositeFarBound)) : ApproximateComparer.Round(new Point(oppositeFarBound, startVertex.Point.Y)); if (PointComparer.Equal(maxDesiredSplicePoint, startVertex.Point)) { // Nothing to do. return; } if (PointComparer.GetPureDirection(startVertex.Point, maxDesiredSplicePoint) != dir) { // It's in the opposite direction, so no need to do anything. return; } // If maxDesiredSplicePoint is shorter, create a new shorter segment. We have to pass both segments // through to the worker function so it knows whether it can go past maxDesiredSegment (which may be limited // by limitRect). var maxDesiredSegment = maxVisibilitySegment; if (PointComparer.GetDirections(maxDesiredSplicePoint, maxDesiredSegment.End) == dir) { maxDesiredSegment = new LineSegment(maxDesiredSegment.Start, maxDesiredSplicePoint); } ExtendEdgeChain(startVertex, dir, maxDesiredSegment, maxVisibilitySegment, pacList, isOverlapped); }
// 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); }
// Return value is whether or not we added a new segment. bool AddSegment(Point start, Point end, Obstacle eventObstacle , BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide , SweepEvent action, double weight) { DevTraceInfoVgGen(1, "Adding Segment [{0} -> {1} {2}] weight {3}", start, end, weight); DevTraceInfoVgGen(2, " side {0}", lowNborSide); DevTraceInfoVgGen(2, " -> side {0}", highNborSide); if (PointComparer.Equal(start, end)) { return(false); } // See if the new segment subsumes or can be subsumed by the last one. gbcList may be null. PointAndCrossingsList gbcList = CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(start, end); bool extendStart, extendEnd; bool wasSubsumed = ScanSegment.Subsume(ref hintScanSegment, start, end, weight, gbcList, ScanDirection , ParallelScanSegments, out extendStart, out extendEnd); if (!wasSubsumed) { Debug.Assert((weight != ScanSegment.ReflectionWeight) || (ParallelScanSegments.Find(start, end) == null), "Reflection segments already in the ScanSegmentTree should should have been detected before calling AddSegment"); hintScanSegment = ParallelScanSegments.InsertUnique(new ScanSegment(start, end, weight, gbcList)).Item; } else if (weight == ScanSegment.ReflectionWeight) { // Do not continue this; it is probably a situation where a side is at a tiny angle from the axis, // resulting in an initial reflection segment that is parallel and very close to the extreme-vertex-derived // segment, so as the staircase progresses they eventually converge due to floating-point rounding. // See RectilinearFilesTest.ReflectionStaircasesConverge. return(false); } // Do reflections only if the new segment is not overlapped. if (ScanSegment.OverlappedWeight != weight) { // If these fire, it's probably an indication that isOverlapped is not correctly set // and one of the neighbors is an OverlapSide from CreateScanSegments. Debug.Assert(lowNborSide is HighObstacleSide, "lowNbor is not HighObstacleSide"); Debug.Assert(highNborSide is LowObstacleSide, "highNbor is not LowObstacleSide"); // If we are closing the obstacle then the initial Obstacles of the reflections (the ones it // will bounce between) are the opposite neighbors. Otherwise, the OpenVertexEvent obstacle // is the ReflectionEvent initial obstacle. if (action is CloseVertexEvent) { // If both neighbor sides reflect upward, they can't intersect, so we don't need // to store a lookahead site (if neither reflect upward, StoreLookaheadSite no-ops). if (!SideReflectsUpward(lowNborSide) || !SideReflectsUpward(highNborSide)) { // Try to store both; only one will "take" (for the upward-reflecting side). // The initial Obstacle is the opposite neighbor. if (extendStart) { this.StoreLookaheadSite(highNborSide.Obstacle, lowNborSide, start, wantExtreme: false); } if (extendEnd) { this.StoreLookaheadSite(lowNborSide.Obstacle, highNborSide, end, wantExtreme: false); } } } else { if (extendStart) { StoreLookaheadSite(eventObstacle, LowNeighborSides.GroupSideInterveningBeforeLowNeighbor, lowNborSide, start); } if (extendEnd) { StoreLookaheadSite(eventObstacle, HighNeighborSides.GroupSideInterveningBeforeHighNeighbor, highNborSide, end); } } } DevTraceInfoVgGen(2, "HintScanSegment {0}{1}", hintScanSegment, wasSubsumed ? " (subsumed)" : ""); DevTrace_DumpScanSegmentsDuringAdd(3); return(true); }
/// <summary> /// Create a LineSegment that contains the max visibility from startPoint in the desired direction. /// </summary> internal LineSegment CreateMaxVisibilitySegment(Point startPoint, Directions dir, out PointAndCrossingsList pacList) { var graphBoxBorderIntersect = StaticGraphUtility.RectangleBorderIntersect(this.GraphBox, startPoint, dir); if (PointComparer.GetDirections(startPoint, graphBoxBorderIntersect) == Directions.None) { pacList = null; return(new LineSegment(startPoint, startPoint)); } var segment = this.RestrictSegmentWithObstacles(startPoint, graphBoxBorderIntersect); // Store this off before other operations which overwrite it. pacList = this.CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(segment.Start, segment.End); return(segment); }
// 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; }