Esempio n. 1
0
 // 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;
 }
Esempio n. 5
0
 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);
                }
            }
        }
Esempio n. 7
0
        // 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);
                }
            }
        }
 // 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);
     }
 }