예제 #1
0
 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);
        }
예제 #4
0
        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);
 }
예제 #8
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);
                }
            }
        }
        // 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);
 }
예제 #11
0
        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);
            }
        }
예제 #12
0
 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);
 }
예제 #13
0
 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();
        }
 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 abstract void ProcessVertexEvent(RBNode<BasicObstacleSide> lowSideNode,
             RBNode<BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent);
예제 #17
0
        private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, BasicVertexEvent vertexEvent)
        {
            Point intersect = ScanLineIntersectSide(vertexEvent.Site, side);

            return(IntersectionAtSideIsInsideAnotherObstacle(side, vertexEvent.Obstacle, intersect));
        }