// 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); } }
} // end ProcessEvent(OpenVertexEvent) void ProcessEvent(LowBendVertexEvent lowVertEvent) { // Note: we only draw lines only from "interesting" vertices, which would be those // that open or close an obstacle, as well as staircase Reflections (see doc). This means // Low/HighVertexEvents routines will just track the change in ActiveLowSide/ActiveHighSide. // This is a vertex on the low side of the obstacle. Update the ActiveLowSide in the obstacle // and scanline (this also checks for Reflection events). var obstacle = lowVertEvent.Obstacle; var lowSide = new LowObstacleSide(obstacle, lowVertEvent.Vertex, ScanDirection); // If the new lowSide is flat we don't remove it due to potential overlaps; that lets any collinear // OpenVertexEvents know they are overlapped (touching == overlap). When we get to CloseVertexEvent, // keeping the current ActiveLowSide in the scanline tells us how when the interior sides stop // so we know when we've found a neighbor. Similarly, if we're turning down toward the // scanline, we let CloseVertexEvent remove the side (in case there are coincident vertices). // That leaves the case of still ascending, where we replace the side in the scanline. if (ScanDirection.ComparePerpCoord(lowSide.End, lowSide.Start) > 0) { this.RemoveSideFromScanLine(this.scanLine.Find(obstacle.ActiveLowSide), lowVertEvent.Site); AddSideToScanLine(lowSide, lowVertEvent.Site); obstacle.ActiveLowSide = lowSide; EnqueueLowBendVertexEvent(lowSide); } } // end ProcessEvent(LowBendVertexEvent)
internal LowReflectionEvent(BasicReflectionEvent previousSite, LowObstacleSide targetSide, Point site) : base(previousSite, targetSide.Obstacle, site) { this.Side = targetSide; }
internal LowReflectionEvent(BasicReflectionEvent previousSite, LowObstacleSide 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); } } }