// Set the initial ActiveLowSide and ActiveHighSide of the obstacle starting at this point. internal void CreateInitialSides(PolylinePoint startPoint, ScanDirection scanDir) { Debug.Assert((null == ActiveLowSide) && (null == ActiveHighSide) , "Cannot call SetInitialSides when sides are already set"); ActiveLowSide = new LowObstacleSide(this, startPoint, scanDir); ActiveHighSide = new HighObstacleSide(this, startPoint, scanDir); if (scanDir.IsFlat(ActiveHighSide)) { // No flat sides in the scanline; we'll do lookahead processing in the scanline to handle overlaps // with existing segments, and normal neighbor handling will take care of collinear OpenVertexEvents. ActiveHighSide = new HighObstacleSide(this, ActiveHighSide.EndVertex, scanDir); } }
void ProcessEvent(HighBendVertexEvent highVertEvent) { // See comments in LowBendVertexEvent; this is mostly the same thing to the other side. var obstacle = highVertEvent.Obstacle; var highSide = new HighObstacleSide(obstacle, highVertEvent.Vertex, ScanDirection); this.RemoveSideFromScanLine(this.scanLine.Find(obstacle.ActiveHighSide), highVertEvent.Site); RBNode<BasicObstacleSide> highSideNode = AddSideToScanLine(highSide, highVertEvent.Site); obstacle.ActiveHighSide = highSide; EnqueueHighBendOrCloseVertexEvent(obstacle.ActiveHighSide); // If this is an extreme high-side lateral vertex on the horizontal pass turning to an upward-reflecting // side - i.e. if it is a vertex on the right-most border of the bounding box, and the new HighObstacleSide // has negative slope - then we may have a situation where a neighbor LowObstacleSide reflects downward and // spans this entire HighObstacleSide and a little more: // # // # // . # // \ # // . # // # // The ".\." side is completely spanned by the '#' side and because of the tilt directions, lookahead // will not happen, therefore reflections will not happen and there will be no scansegments between the // two sides. This could lead to spurious overlaps. So in this case we drop in an extra lookahead. // (This is not an issue for other tilt directions - there is always a reflection chain generated). // Test is RectilinearFileTests.Overlap_ExtremeSide_Lookahead. if (this.wantReflections && this.ScanDirection.IsHorizontal && (highSide.Start.X == obstacle.VisibilityBoundingBox.Right) && SideReflectsUpward(highSide)) { var nborSideNode = this.scanLine.NextHigh(highSideNode); if ((nborSideNode.Item is LowObstacleSide) && this.SideReflectsDownward(nborSideNode.Item)) { if (!obstacle.IsOverlapped || !this.ObstacleTree.PointIsInsideAnObstacle(highSide.Start, this.ScanDirection)) { this.StoreLookaheadSite(nborSideNode.Item.Obstacle, highSide, highSide.Start, wantExtreme: true); LoadReflectionEvents(nborSideNode.Item); } } } }
void ProcessEvent(OpenVertexEvent openVertEvent) { // First insert the two new lines into the scanline. Note: Although the lines are clockwise oriented, // LowObstacleSide and HighObstacleSide take a parameter to know when to go counterclockwise. var obstacle = openVertEvent.Obstacle; obstacle.CreateInitialSides(openVertEvent.Vertex, ScanDirection); Debug.Assert(!IsFlat(obstacle.ActiveLowSide), "OpenVertexEvent ActiveLowSide should not be flat"); Debug.Assert(!IsFlat(obstacle.ActiveHighSide), "RemoveCollinearSides should have been called"); DevTraceIfFlatSide(true /*isObstacleOpen*/, obstacle.ActiveLowSide.Start, obstacle.ActiveHighSide.Start); // Adding can rotate the RBTree which modifies RBNodes so get the lowSideNode after adding highSideNode. // AddSideToScanLine loads any reflection events for the side. AddSideToScanLine(obstacle.ActiveLowSide, openVertEvent.Site); RBNode<BasicObstacleSide> highSideNode = AddSideToScanLine(obstacle.ActiveHighSide, openVertEvent.Site); RBNode<BasicObstacleSide> lowSideNode = scanLine.Find(obstacle.ActiveLowSide); // Get the neighbors. In the simple, non-overlapped case, we'll generate a segment between them which // includes the vertex point (and any flat border of the current obstacle). These neighbors will // never be null; one or both may be the fake sentinel borders at the graphBox limits. this.FindNeighborsAndProcessVertexEvent(lowSideNode, highSideNode, openVertEvent); // Look for Reflections. If the ScanSegments we just added generated any // ReflectionEvents, then the Active*Side to that side may cover them, or if we're overlapped, // the Active*Side of the overlapping obstacle may if there is a non-overlapped segment extension. // Check the neighbors in both directions. var lowReflector = LowNeighborSides.GroupSideInterveningBeforeLowNeighbor ?? LowNeighborSides.LowNeighborSide; if (SideReflectsUpward(lowReflector)) { LoadReflectionEvents(obstacle.ActiveLowSide); } var highReflector = HighNeighborSides.GroupSideInterveningBeforeHighNeighbor ?? HighNeighborSides.HighNeighborSide; if (SideReflectsUpward(highReflector)) { LoadReflectionEvents(obstacle.ActiveHighSide); } // If this is a flat side it must absorb any outstanding reflection sites. if (obstacle.ActiveHighSide.Start != obstacle.ActiveLowSide.Start) { // Create a temp HighObstacleSide so the "next vertex" moves in the correct direction. var tempSide = new HighObstacleSide(obstacle, openVertEvent.Vertex, ScanDirection); lookaheadScan.RemoveSitesForFlatBottom(tempSide.Start, tempSide.End); } // Add events for the low and high sides. EnqueueLowBendVertexEvent(obstacle.ActiveLowSide); EnqueueHighBendOrCloseVertexEvent(obstacle.ActiveHighSide); } // end ProcessEvent(OpenVertexEvent)
internal HighReflectionEvent(BasicReflectionEvent previousSite, HighObstacleSide targetSide, Point site) : base (previousSite, targetSide.Obstacle, site) { this.Side = targetSide; }
internal HighReflectionEvent(BasicReflectionEvent previousSite, HighObstacleSide targetSide, Point site) : base(previousSite, targetSide.Obstacle, site) { this.Side = targetSide; }
// 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); } } }