void CreateScanSegments(Obstacle obstacle, NeighborSides neighborSides, BasicVertexEvent vertexEvent) { this.CreateScanSegments(obstacle, (HighObstacleSide)neighborSides.LowNeighbor.Item , (null == neighborSides.LowOverlapEnd) ? null : neighborSides.LowOverlapEnd.Item , (null == neighborSides.HighOverlapEnd) ? null : neighborSides.HighOverlapEnd.Item , (LowObstacleSide)neighborSides.HighNeighbor.Item, vertexEvent); }
protected void FindNeighbors(BasicVertexEvent vertexEvent, RBNode<BasicObstacleSide> sideNode, NeighborSides neighborSides) { // vertexEvent.Site is on one of vertexEvent.Obstacle.Active(Low|High)Side, so we must get the // appropriate vertex on whichever one of those Active*Sides is sideNode. var sideReferencePoint = (vertexEvent is OpenVertexEvent) ? sideNode.Item.Start : sideNode.Item.End; RBNode<BasicObstacleSide> initialLowNbor, initialHighNbor; this.FindInitialNeighborSides(sideNode, out initialLowNbor, out initialHighNbor); this.SkipToNeighbor(this.ScanDirection.OppositeDirection, sideNode.Item, sideReferencePoint, initialLowNbor, neighborSides); this.SkipToNeighbor(this.ScanDirection.Direction, sideNode.Item, sideReferencePoint, initialHighNbor, neighborSides); }
// As described in the doc, we stop at the first neighbor of the appropriate side type that we touch // the border of, even if that's just skimming along the extreme vertex of it, because those will // continue the chain of open/close+addSegment, and we don't want to follow the full length of the // segment each time if there are a lot of collinear obstacle open/close events. protected void FindNeighbors(BasicVertexEvent vertexEvent, RBNode<BasicObstacleSide> lowSideNode, RBNode<BasicObstacleSide> highSideNode) { LowNeighborSides.Clear(); HighNeighborSides.Clear(); // Find the first HighObstacleSide in the low (scanline-decreasing) direction (this may be the low // sentinel) and the lowest LowObstacleSide toward that that we cross *through*, if any. Then do // the same thing in the high direction. If we are not overlapped, then we'll jump out immediately // from SkipToNeighbor, so there won't be a lot of redundant effort in that case. FindNeighbors(vertexEvent, lowSideNode, LowNeighborSides); FindNeighbors(vertexEvent, highSideNode, HighNeighborSides); }
protected override void ProcessVertexEvent(RBNode <BasicObstacleSide> lowSideNode, RBNode <BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { var vertexPoints = (base.ScanDirection.IsHorizontal) ? this.horizontalVertexPoints : this.verticalVertexPoints; vertexPoints.Insert(vertexEvent.Site); // For easier reading... var lowNborSide = LowNeighborSides.LowNeighbor.Item; var highNborSide = HighNeighborSides.HighNeighbor.Item; var highDir = base.ScanDirection.Direction; var lowDir = base.ScanDirection.OppositeDirection; // Generate the neighbor side intersections, regardless of overlaps; these are the type-2 Steiner points. var lowSteiner = ScanLineIntersectSide(vertexEvent.Site, lowNborSide); var highSteiner = ScanLineIntersectSide(vertexEvent.Site, highNborSide); // Add the intersections at the neighbor bounding boxes if the intersection is not at a sentinel. // Go in the opposite direction from the neighbor intersection to find the border between the Steiner // point and vertexEvent.Site (unless vertexEvent.Site is inside the bounding box). if (ObstacleTree.GraphBox.Contains(lowSteiner)) { var bboxIntersectBeforeLowSteiner = StaticGraphUtility.RectangleBorderIntersect(lowNborSide.Obstacle.VisibilityBoundingBox, lowSteiner, highDir); if (PointComparer.IsPureLower(bboxIntersectBeforeLowSteiner, vertexEvent.Site)) { this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeLowSteiner); } } if (ObstacleTree.GraphBox.Contains(highSteiner)) { var bboxIntersectBeforeHighSteiner = StaticGraphUtility.RectangleBorderIntersect(highNborSide.Obstacle.VisibilityBoundingBox, highSteiner, lowDir); if (PointComparer.IsPureLower(vertexEvent.Site, bboxIntersectBeforeHighSteiner)) { this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeHighSteiner); } } // Add the corners of the bounding box of the vertex obstacle, if they are visible to the event site. // This ensures that we "go around" the obstacle, as with the non-orthogonal edges in the paper. Point lowCorner, highCorner; GetBoundingCorners(lowSideNode.Item.Obstacle.VisibilityBoundingBox, vertexEvent is OpenVertexEvent, this.ScanDirection.IsHorizontal, out lowCorner, out highCorner); if (PointComparer.IsPureLower(lowSteiner, lowCorner) || lowNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle)) { vertexPoints.Insert(lowCorner); } if (PointComparer.IsPureLower(highCorner, highSteiner) || highNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle)) { vertexPoints.Insert(highCorner); } }
protected override void ProcessVertexEvent(RBNode<BasicObstacleSide> lowSideNode, RBNode<BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { // Create the scan segment from the low side. CreateScanSegmentFromLowSide(lowSideNode, vertexEvent); // If the low segment covered up to our high neighbor, we're done. Otherwise, there were overlaps // inside a flat boundary and now we need to come in from the high side. In this case there's a chance // that we're redoing a single subsegment in the event of two obstacles' outside edges crossing // the middle of a flat boundary of the event obstacle, but that should be sufficiently rare that // we don't need to optimize it away as the segments will be merged by ScanSegmentTree.MergeSegments. // TODOgroup TODOperf: currentGroupBoundaryCrossingMap still has the Low-side stuff in it but it shouldn't // matter much - profile to see how much time GetOrderedIndexBetween takes. if (LowNeighborSides.HighNeighbor.Item != HighNeighborSides.HighNeighbor.Item) { CreateScanSegmentFromHighSide(highSideNode, vertexEvent); } }
void CreateScanSegmentFromHighSide(RBNode<BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { // Create one or more segments from low to high using the neighbors of the HighObstacleSide. this.CreateScanSegments(highSideNode.Item.Obstacle, this.HighNeighborSides, vertexEvent); }
private void CreateScanSegmentFromLowSide(RBNode<BasicObstacleSide> lowSideNode, BasicVertexEvent vertexEvent) { // Create one or more segments from low to high using the neighbors of the LowObstacleSide. this.CreateScanSegments(lowSideNode.Item.Obstacle, this.LowNeighborSides, vertexEvent); }
// As described in the document, we currently don't create ScanSegments where a flat top/bottom boundary may have // intervals that are embedded within overlapped segments: // obstacle1 | |obstacle2 | obstacle3 | | obstacle4 | obstacle2| | // | +-----------|===========|??????????|===========|----------+ | obstacle5 // ...__________| |___________| |___________| |__________... // Here, there will be no ScanSegment created at ??? along the border of obstacle2 between obstacle3 // and obstacle4. This is not a concern because that segment is useless anyway; a path from outside // obstacle2 will not be able to see it unless there is another obstacle in that gap, and then that // obstacle's side-derived ScanSegments will create non-overlapped edges; and there are already edges // from the upper/lower extreme vertices of obstacle 3 and obstacle4 to ensure a connected graph. // If there is a routing from an obstacle outside obstacle2 to one embedded within obstacle2, the // port visibility will create the necessary edges. // // We don't try to determine nesting depth and create different VisibilityEdge weights to prevent spurious // nested-obstacle crossings; we just care about overlapped vs. not-overlapped. // If this changes, we would have to: Find the overlap depth at the lowNborSide intersection, // then increment/decrement according to side type as we move from low to high, then create a different // ScanSegment at each obstacle-side crossing, making ScanSegment.IsOverlapped a depth instead of bool. // Then pass that depth through to VisibilityEdge as an increased weight. (This would also automatically // handle the foregoing situation of non-overlapped intervals in the middle of a flat top/bottom border, // not that it would really gain anything). void CreateScanSegments(Obstacle obstacle, HighObstacleSide lowNborSide, BasicObstacleSide lowOverlapSide, BasicObstacleSide highOverlapSide, LowObstacleSide highNborSide, BasicVertexEvent vertexEvent) { // If we have either of the high/low OverlapSides, we'll need to see if they're inside // another obstacle. If not, they end the overlap. if ((null == highOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(highOverlapSide, vertexEvent)) { highOverlapSide = highNborSide; } if ((null == lowOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(lowOverlapSide, vertexEvent)) { lowOverlapSide = lowNborSide; } // There may be up to 3 segments; for a simple diagram, |# means low-side border of // obstacle '#' and #| means high-side, with 'v' meaning our event vertex. Here are // the two cases where we create a single non-overlapped ScanSegment (from the Low // side in the diagram, but the same logic works for the High side). // - non-overlapped: 1| v |2 // ...---+ +---... // - non-overlapped to an "inner" highNbor on a flat border: 1| vLow |2 vHigh // ...---+ +-----+=========... // This may be the low side of a flat bottom or top border, so lowNbor or highNbor // may be in the middle of the border. Point lowNborIntersect = ScanLineIntersectSide(vertexEvent.Site, lowNborSide); Point highNborIntersect = ScanLineIntersectSide(vertexEvent.Site, highNborSide); bool lowNborEndpointIsOverlapped = IntersectionAtSideIsInsideAnotherObstacle(lowNborSide, vertexEvent.Obstacle /*obstacleToIgnore*/, lowNborIntersect); if (!lowNborEndpointIsOverlapped && (lowNborSide == lowOverlapSide)) { // Nothing is overlapped so create one segment. AddSegment(lowNborIntersect, highNborIntersect, obstacle, lowNborSide, highNborSide, vertexEvent, ScanSegment.NormalWeight); return; } // Here are the different interval combinations for overlapped cases. // - non-overlapped, overlapped: 1| |2 v |3 // ...---+ +------+===... // - non-overlapped, overlapped, non-overlapped: 1| |2 v 2| |3 // ==+ +-------+ +--... // - overlapped: |1 2| v |3 ...1| // ...---+====+-----+===...-+ // - overlapped, non-overlapped: |1 2| v 1| |3 // ...---+====+------+ +---... // We will not start overlapped and then go to non-overlapped and back to overlapped, // because we would have found the overlap-ending/beginning sides as nearer neighbors. // Get the other intersections we'll want. Point highOverlapIntersect = (highOverlapSide == highNborSide) ? highNborIntersect : ScanLineIntersectSide(vertexEvent.Site, highOverlapSide); Point lowOverlapIntersect = (lowOverlapSide == lowNborSide) ? lowNborIntersect : ScanLineIntersectSide(vertexEvent.Site, lowOverlapSide); // Create the segments. if (!lowNborEndpointIsOverlapped) { // First interval is non-overlapped; there is a second overlapped interval, and may be a // third non-overlapping interval if another obstacle surrounded this vertex. AddSegment(lowNborIntersect, lowOverlapIntersect, obstacle, lowNborSide, lowOverlapSide, vertexEvent, ScanSegment.NormalWeight); AddSegment(lowOverlapIntersect, highOverlapIntersect, obstacle, lowOverlapSide, highOverlapSide, vertexEvent, ScanSegment.OverlappedWeight); if (highOverlapSide != highNborSide) { AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent, ScanSegment.NormalWeight); } } else { // Starts off overlapped so ignore lowOverlapSide. AddSegment(lowNborIntersect, highOverlapIntersect, obstacle, lowNborSide, highOverlapSide, vertexEvent, ScanSegment.OverlappedWeight); if (highOverlapSide != highNborSide) { AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent, ScanSegment.NormalWeight); } } }
private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, BasicVertexEvent vertexEvent) { Point intersect = ScanLineIntersectSide(vertexEvent.Site, side); return IntersectionAtSideIsInsideAnotherObstacle(side, vertexEvent.Obstacle, intersect); }
protected override void ProcessVertexEvent(RBNode <BasicObstacleSide> lowSideNode, RBNode <BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { // Create the scan segment from the low side. CreateScanSegmentFromLowSide(lowSideNode, vertexEvent); // If the low segment covered up to our high neighbor, we're done. Otherwise, there were overlaps // inside a flat boundary and now we need to come in from the high side. In this case there's a chance // that we're redoing a single subsegment in the event of two obstacles' outside edges crossing // the middle of a flat boundary of the event obstacle, but that should be sufficiently rare that // we don't need to optimize it away as the segments will be merged by ScanSegmentTree.MergeSegments. // TODOgroup TODOperf: currentGroupBoundaryCrossingMap still has the Low-side stuff in it but it shouldn't // matter much - profile to see how much time GetOrderedIndexBetween takes. if (LowNeighborSides.HighNeighbor.Item != HighNeighborSides.HighNeighbor.Item) { CreateScanSegmentFromHighSide(highSideNode, vertexEvent); } }
void CreateScanSegmentFromHighSide(RBNode <BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { // Create one or more segments from low to high using the neighbors of the HighObstacleSide. this.CreateScanSegments(highSideNode.Item.Obstacle, this.HighNeighborSides, vertexEvent); }
private void CreateScanSegmentFromLowSide(RBNode <BasicObstacleSide> lowSideNode, BasicVertexEvent vertexEvent) { // Create one or more segments from low to high using the neighbors of the LowObstacleSide. this.CreateScanSegments(lowSideNode.Item.Obstacle, this.LowNeighborSides, vertexEvent); }
void FindNeighborsAndProcessVertexEvent(RBNode<BasicObstacleSide> lowSideNode , RBNode<BasicObstacleSide> highSideNode , BasicVertexEvent vertexEvent) { CurrentGroupBoundaryCrossingMap.Clear(); FindNeighbors(vertexEvent, lowSideNode, highSideNode); this.ProcessVertexEvent(lowSideNode, highSideNode, vertexEvent); // Clear this again because we don't want Reflections to access stale values. CurrentGroupBoundaryCrossingMap.Clear(); }
protected abstract void ProcessVertexEvent(RBNode<BasicObstacleSide> lowSideNode, RBNode<BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent);
private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, BasicVertexEvent vertexEvent) { Point intersect = ScanLineIntersectSide(vertexEvent.Site, side); return(IntersectionAtSideIsInsideAnotherObstacle(side, vertexEvent.Obstacle, intersect)); }