internal void CreateObstaclePorts(Obstacle obstacle) {
     // Create ObstaclePorts for all Ports of this obstacle.  This just creates the
     // ObstaclePort object; we don't add its edges/vertices to the graph until we
     // do the actual routing.
     foreach (var port in obstacle.Ports) {
         CreateObstaclePort(obstacle, port);
     }
 }
 protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
         BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action) {
     // See notes in InsertPerpendicularReflectionSegment for comments about an existing segment.
     // Here, we call AddSegment which adds the segment and continues the reflection staircase.
     if (null != ParallelScanSegments.Find(start, end)) {
         return false;
     }
     return AddSegment(start, end, eventObstacle, lowNborSide, highNborSide, action, ScanSegment.ReflectionWeight);
 }
 internal BasicObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir, bool traverseClockwise)
     : base(startVertex)
 {
     Obstacle = obstacle;
     endVertex = traverseClockwise ? startVertex.NextOnPolyline : startVertex.PrevOnPolyline;
     if (!scanDir.IsPerpendicular(startVertex.Point, endVertex.Point))
     {
         Slope = StaticGraphUtility.Slope(startVertex.Point, endVertex.Point, scanDir);
         SlopeInverse = 1.0 / Slope;
     }
 }
        internal GroupBoundaryCrossing AddIntersection(Point intersection, Obstacle group, Directions dirToInside) {
            List<GroupBoundaryCrossing> crossings;
            if (!pointCrossingMap.TryGetValue(intersection, out crossings)) {
                crossings = new List<GroupBoundaryCrossing>();
                pointCrossingMap[intersection] = crossings;
            }

            // We may hit the same point on neighbor traversal in multiple directions.  We will have more than one item
            // in this list only if there are multiple group boundaries at this point, which should be unusual.
            var crossingsCount = crossings.Count;       // cache for perf
            for (int ii = 0; ii < crossingsCount; ++ii) {
                var crossing = crossings[ii];
                if (crossing.Group == group) {
                    // At a given location for a given group, there is only one valid dirToInside.
                    Debug.Assert(dirToInside == crossing.DirectionToInside, "Mismatched dirToInside");
                    return crossing;
                }
            }
            var newCrossing = new GroupBoundaryCrossing(group, dirToInside);
            crossings.Add(newCrossing);
            return newCrossing;
        }
        private ObstaclePort CreateObstaclePort(Obstacle obstacle, Port port) {
            // This will replace any previous specification for the port (last one wins).
            Debug.Assert(!obstaclePortMap.ContainsKey(port), "Port is used by more than one obstacle");

            if (null == port.Curve) {
                return null;
            }

            var roundedLocation = ApproximateComparer.Round(port.Location);
            if (PointLocation.Outside == Curve.PointRelativeToCurveLocation(roundedLocation, obstacle.InputShape.BoundaryCurve)) {
                // Obstacle.Port is outside Obstacle.Shape; handle it as a FreePoint.
                return null;
            }

            if ((obstacle.InputShape.BoundaryCurve != port.Curve) 
                && (PointLocation.Outside == Curve.PointRelativeToCurveLocation(roundedLocation, port.Curve))) {
                // Obstacle.Port is outside port.Curve; handle it as a FreePoint.
                return null;
            }

            var oport = new ObstaclePort(port, obstacle);
            obstaclePortMap[port] = oport;
            return oport;
        }
示例#6
0
 internal BasicVertexEvent(Obstacle obstacle, PolylinePoint p) : base(p)
 {
     this.Obstacle = obstacle;
 }
 internal static bool ObstaclesIntersect(Obstacle first, Obstacle second) 
 {
     if (first == second) 
     {
         return false;
     }
     return Curve.CurvesIntersect(first.VisibilityPolyline, second.VisibilityPolyline);    
 }
 private void CreatePaddedObstacle(Shape shape) {
     var obstacle = new Obstacle(shape, this.UseObstacleRectangles, this.Padding);
     this.ShapeToObstacleMap[shape] = obstacle;
     this.PortManager.CreateObstaclePorts(obstacle);
 }
示例#9
0
 internal CloseVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p)
 {
 }
 internal HighObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsVertical /*traverseClockwise*/)
 {
 }
示例#11
0
        protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
                                                                BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action)
        {
            Debug.Assert(false, "base.wantReflections is false in Sparse mode so this should never be called");
// ReSharper disable HeuristicUnreachableCode
            return(false);
// ReSharper restore HeuristicUnreachableCode
        }
 // If true, we have a staircase situation.
 internal bool IsStaircaseStep(Obstacle reflectionTarget) {
     return (this.InitialObstacle == reflectionTarget);
 }
示例#13
0
        // obstacleToIgnore is the event obstacle if we're looking at intersections along its boundary.
        private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, Obstacle eventObstacle, Point intersect)
        {
            // See if the intersection with an obstacle side is inside another obstacle (that encloses
            // at least the part of side.Obstacle containing the intersection).  This will only happen
            // if side.Obstacle is overlapped and in the same clump (if it's not the same clump, we must
            // be hitting it from the outside).
            if (!side.Obstacle.IsOverlapped)
            {
                return(false);
            }

            if (!side.Obstacle.IsGroup && !eventObstacle.IsGroup && (side.Obstacle.Clump != eventObstacle.Clump))
            {
                return(false);
            }

            return(ObstacleTree.IntersectionIsInsideAnotherObstacle(side.Obstacle, eventObstacle, intersect, ScanDirection));
        }
示例#14
0
        // Return value is whether or not we added a new segment.
        bool AddSegment(Point start, Point end, Obstacle eventObstacle
                        , BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide
                        , SweepEvent action, double weight)
        {
            DevTraceInfoVgGen(1, "Adding Segment [{0} -> {1} {2}] weight {3}", start, end, weight);
            DevTraceInfoVgGen(2, "     side {0}", lowNborSide);
            DevTraceInfoVgGen(2, "  -> side {0}", highNborSide);
            if (PointComparer.Equal(start, end))
            {
                return(false);
            }

            // See if the new segment subsumes or can be subsumed by the last one.  gbcList may be null.
            PointAndCrossingsList gbcList = CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(start, end);
            bool extendStart, extendEnd;
            bool wasSubsumed = ScanSegment.Subsume(ref hintScanSegment, start, end, weight, gbcList, ScanDirection
                                                   , ParallelScanSegments, out extendStart, out extendEnd);

            if (!wasSubsumed)
            {
                Debug.Assert((weight != ScanSegment.ReflectionWeight) || (ParallelScanSegments.Find(start, end) == null),
                             "Reflection segments already in the ScanSegmentTree should should have been detected before calling AddSegment");
                hintScanSegment = ParallelScanSegments.InsertUnique(new ScanSegment(start, end, weight, gbcList)).Item;
            }
            else if (weight == ScanSegment.ReflectionWeight)
            {
                // Do not continue this; it is probably a situation where a side is at a tiny angle from the axis,
                // resulting in an initial reflection segment that is parallel and very close to the extreme-vertex-derived
                // segment, so as the staircase progresses they eventually converge due to floating-point rounding.
                // See RectilinearFilesTest.ReflectionStaircasesConverge.
                return(false);
            }

            // Do reflections only if the new segment is not overlapped.
            if (ScanSegment.OverlappedWeight != weight)
            {
                // If these fire, it's probably an indication that isOverlapped is not correctly set
                // and one of the neighbors is an OverlapSide from CreateScanSegments.
                Debug.Assert(lowNborSide is HighObstacleSide, "lowNbor is not HighObstacleSide");
                Debug.Assert(highNborSide is LowObstacleSide, "highNbor is not LowObstacleSide");

                // If we are closing the obstacle then the initial Obstacles of the reflections (the ones it
                // will bounce between) are the opposite neighbors.  Otherwise, the OpenVertexEvent obstacle
                // is the ReflectionEvent initial obstacle.
                if (action is CloseVertexEvent)
                {
                    // If both neighbor sides reflect upward, they can't intersect, so we don't need
                    // to store a lookahead site (if neither reflect upward, StoreLookaheadSite no-ops).
                    if (!SideReflectsUpward(lowNborSide) || !SideReflectsUpward(highNborSide))
                    {
                        // Try to store both; only one will "take" (for the upward-reflecting side).
                        // The initial Obstacle is the opposite neighbor.
                        if (extendStart)
                        {
                            this.StoreLookaheadSite(highNborSide.Obstacle, lowNborSide, start, wantExtreme: false);
                        }

                        if (extendEnd)
                        {
                            this.StoreLookaheadSite(lowNborSide.Obstacle, highNborSide, end, wantExtreme: false);
                        }
                    }
                }
                else
                {
                    if (extendStart)
                    {
                        StoreLookaheadSite(eventObstacle, LowNeighborSides.GroupSideInterveningBeforeLowNeighbor, lowNborSide, start);
                    }

                    if (extendEnd)
                    {
                        StoreLookaheadSite(eventObstacle, HighNeighborSides.GroupSideInterveningBeforeHighNeighbor, highNborSide, end);
                    }
                }
            }

            DevTraceInfoVgGen(2, "HintScanSegment {0}{1}", hintScanSegment, wasSubsumed ? " (subsumed)" : "");
            DevTrace_DumpScanSegmentsDuringAdd(3);
            return(true);
        }
示例#15
0
 internal LowObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsHorizontal /*traverseClockwise*/)
 {
 }
 internal HighObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsVertical /*traverseClockwise*/)
 {
 }
 internal LowObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsHorizontal /*traverseClockwise*/)
 {
 }
 internal BasicVertexEvent(Obstacle obstacle, PolylinePoint p) : base (p) {
     this.Obstacle = obstacle;
 }
 internal ObstaclePort(Port port, Obstacle obstacle) {
     this.Port = port;
     this.Obstacle = obstacle;
     this.PortEntrances = new List<ObstaclePortEntrance>();
     this.Location = ApproximateComparer.Round(this.Port.Location);
 }
        private static bool ObstaclesAreCloseEnoughToBeConsideredTouching(Obstacle a, Obstacle b, bool aIsInsideB, bool bIsInsideA)
        {
            // This is only called when the obstacle.VisibilityPolylines don't intersect, thus one is inside the other
            // or both are outside. If both are outside then either one's LooseVisibilityPolyline may be used.
            if (!aIsInsideB && !bIsInsideA)
            {
                return(Curve.CurvesIntersect(a.LooseVisibilityPolyline, b.VisibilityPolyline));
            }

            // Otherwise see if the inner one is close enough to the outer border to consider them touching.
            var innerLoosePolyline = aIsInsideB ? a.LooseVisibilityPolyline : b.LooseVisibilityPolyline;
            var outerPolyline      = aIsInsideB ? b.VisibilityPolyline : a.VisibilityPolyline;

            foreach (Point innerPoint in innerLoosePolyline)
            {
                if (Curve.PointRelativeToCurveLocation(innerPoint, outerPolyline) == PointLocation.Outside)
                {
                    var outerParamPoint = Curve.ClosestPoint(outerPolyline, innerPoint);
                    if (!ApproximateComparer.CloseIntersections(innerPoint, outerParamPoint))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
示例#21
0
 internal HighBendVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p)
 {
 }
示例#22
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);
                }
            }
        }
 internal static bool IsFirstObstacleEntirelyWithinSecond(Obstacle first, Obstacle second, bool touchingOk) 
 {
     return IsFirstPolylineEntirelyWithinSecond(first.VisibilityPolyline, second.VisibilityPolyline, touchingOk);   
 }
示例#24
0
        protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
                                                                BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action)
        {
            // See notes in InsertPerpendicularReflectionSegment for comments about an existing segment.
            // Here, we call AddSegment which adds the segment and continues the reflection staircase.
            if (null != ParallelScanSegments.Find(start, end))
            {
                return(false);
            }

            return(AddSegment(start, end, eventObstacle, lowNborSide, highNborSide, action, ScanSegment.ReflectionWeight));
        }
 internal LowBendVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
 internal void Insert(Obstacle obstacle) 
 {
     groupsAndClumps.Insert(obstacle);
 }
 // Called by StoreLookaheadSite only.
 internal BasicReflectionEvent(Obstacle initialObstacle, Obstacle reflectingObstacle, Point site) {
     this.InitialObstacle = initialObstacle;
     this.ReflectingObstacle = reflectingObstacle;
     this.site = site;
 }
 private void StoreLookaheadSite(Obstacle eventObstacle, BasicObstacleSide interveningGroupSide, BasicObstacleSide neighborSide, Point siteOnSide) {
     // For reflections, NeighborSides won't be set, so there won't be an intervening group.  Otherwise,
     // this is on an OpenVertexEvent, so we'll either reflect of the intervening group if any, or neighborSide.
     if (null == interveningGroupSide) {
         this.StoreLookaheadSite(eventObstacle, neighborSide, siteOnSide, wantExtreme: false);
     } else {
         var siteOnGroup = ScanLineIntersectSide(siteOnSide, interveningGroupSide, this.ScanDirection);
         this.StoreLookaheadSite(eventObstacle, interveningGroupSide, siteOnGroup, wantExtreme: false);
     }
 }
 internal GroupBoundaryCrossing(Obstacle group, Directions dirToInside) {
     Debug.Assert(CompassVector.IsPureDirection(dirToInside), "Impure direction");
     this.Group = group;
     this.DirectionToInside = dirToInside;
 }
 // obstacleToIgnore is the event obstacle if we're looking at intersections along its boundary.
 private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, Obstacle eventObstacle, Point intersect) {
     // See if the intersection with an obstacle side is inside another obstacle (that encloses
     // at least the part of side.Obstacle containing the intersection).  This will only happen
     // if side.Obstacle is overlapped and in the same clump (if it's not the same clump, we must
     // be hitting it from the outside).
     if (!side.Obstacle.IsOverlapped) {
         return false;
     }
     if (!side.Obstacle.IsGroup && !eventObstacle.IsGroup && (side.Obstacle.Clump != eventObstacle.Clump)) {
         return false;
     }
     return ObstacleTree.IntersectionIsInsideAnotherObstacle(side.Obstacle, eventObstacle, intersect, ScanDirection);
 }
 internal void RemoveObstaclePorts(Obstacle obstacle) {
     foreach (var port in obstacle.Ports) {
         // Since we remove the port from the visibility graph after each routing, all we
         // have to do here is remove it from the dictionary.
         RemoveObstaclePort(port);
     }
 }
        // 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);
                }
            }
        }
 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);
 }
 private bool AddParallelReflectionSegment(Obstacle eventObstacle, BasicObstacleSide lowNborSide
                         , BasicObstacleSide highNborSide, BasicReflectionEvent action) {
     // If this is reflecting to a low neighbor, then that intersect is 'start' in the low-to-high
     // sequence, and the event site is the end; otherwise we start at the event site and end at
     // the high neighbor.
     Point intersect = ScanLineIntersectSide(action.Site, lowNborSide ?? highNborSide);
     Point start = (null != lowNborSide) ? intersect : action.Site;
     Point end = (null != lowNborSide) ? action.Site : intersect;
     
     // Now get the opposite neighbors so AddSegment can continue the reflection chain.
     if (null == lowNborSide) {
         lowNborSide = scanLine.NextLow(highNborSide).Item;
     }
     else {
         highNborSide = scanLine.NextHigh(lowNborSide).Item;
     }
     return InsertParallelReflectionSegment(start, end, eventObstacle, lowNborSide, highNborSide, action);
 }
        private static void GrowGroupAroundLoosePolyline(Obstacle group, Polyline loosePolyline)
        {
            var points = group.VisibilityPolyline.Select(p => p).Concat(loosePolyline.Select(p => p));

            group.SetConvexHull(new OverlapConvexHull(ConvexHull.CreateConvexHullAsClosedPolyline(points), new[] { group }));
        }
 protected bool ScanLineCrossesObstacle(Point eventSite, Obstacle obstacle) {
     // An inner or outer neighbor's side is only an overlap start/stop candidate if its obstacle 
     // brackets the open/close event's Perpendicular Scan coord.
     return (ScanDirection.ComparePerpCoord(eventSite, obstacle.VisibilityBoundingBox.LeftBottom) > 0)
         && (ScanDirection.ComparePerpCoord(eventSite, obstacle.VisibilityBoundingBox.RightTop) < 0);
 }
示例#37
0
 public bool IsInSameClump(Obstacle other)
 {
     return(this.IsOverlapped && (this.Clump == other.Clump));
 }
 // Called by StoreLookaheadSite only.
 internal BasicReflectionEvent(Obstacle initialObstacle, Obstacle reflectingObstacle, Point site)
 {
     this.InitialObstacle    = initialObstacle;
     this.ReflectingObstacle = reflectingObstacle;
     this.site = site;
 }
        // Calculate reflections from the lines, depending on line side (Low vs. High) and slope.
        // Because the low neighbor intersection is on a high side of its obstacle
        // and vice-versa, then the "side" of a lowNbor is a highSide, and vice versa.
        protected void StoreLookaheadSite(Obstacle initialObstacle, BasicObstacleSide reflectingSide, Point reflectionSite, bool wantExtreme) {
            if (!this.wantReflections) {
                return;
            }

            // If the line is perpendicular, we won't generate reflections (they'd be redundant).
            if (!IsPerpendicular(reflectingSide)) {
                // If this is hitting an extreme vertex in the forward direction, we will (or already did) create a normal
                // ScanSegment in the perpendicular direction.
                if (!wantExtreme && !StaticGraphUtility.PointIsInRectangleInterior(reflectionSite, reflectingSide.Obstacle.VisibilityBoundingBox)) {
                    return;
                }

                // We can only do upward reflections, which fortunately is all we need.
                if (SideReflectsUpward(reflectingSide)) {
                    // We defer actually creating the perpendicular line until we've processed
                    // the reflection event we're about to enqueue, so we may legitimately encounter
                    // two (or more) reflections at the same point along the parallel-to-scanline
                    // coordinate, if we have a side that is nearly but not quite perpendicular to
                    // the scanline.  For example:
                    //      \
                    //       \----------------------------- line2
                    //        \---------------------------- line1
                    // Assume the vertical side is very close to perpendicular; then as we process
                    // the horizontal lines in the upward direction, we may generate two lookahead
                    // reflections at coordinates that are sufficiently far apart in the vertical
                    // coordinate (in this example, Y) that the lines are not subsumed, but are
                    // sufficiently close in the horizontal coordinate (in this example, X) that
                    // the perpendicular lines would be subsumed. In that case, we look here to see
                    // if there is an active lookahead scan for the reflecting site's parallel coordinate;
                    // if so, then because we know that the side reflects upward, we also know that
                    // the perpendicular line from the lowest segment's reflection site will intersect
                    // the higher segments here, providing the VisibilityVertices we need, so we can
                    // safely ignore the duplicate lookahead.

                    // Don't worry about enqueueing a reflection at the extreme scanline-parallel
                    // vertex because we'll MergeSegments to handle that.
                    if (null == lookaheadScan.Find(reflectionSite)) {
                        lookaheadScan.Add(new BasicReflectionEvent(initialObstacle, reflectingSide.Obstacle, reflectionSite));
                        DevTraceInfoVgGen(1, "Storing reflection lookahead site {0}", reflectionSite);
                    }
                    else {
                        DevTraceInfoVgGen(1, "Reflection lookahead site {0} already exists", reflectionSite);
                    }
                }
            }
        } // end StoreLookaheadSite()
 // Called by LowReflectionEvent or HighReflectionEvent ctors, which are called out of 
 // AddReflectionEvent, which in turn is called by LoadLookaheadIntersections.
 // In this case we know the eventObstacle and initialObstacle are the same obstacle (the
 // one that the reflected ray bounced off of, to generate the Left/HighReflectionEvent).
 internal BasicReflectionEvent(BasicReflectionEvent previousSite, Obstacle reflectingObstacle, Point site) {
     this.InitialObstacle = previousSite.ReflectingObstacle;
     this.ReflectingObstacle = reflectingObstacle;
     this.site = site;
     this.PreviousSite = previousSite;
 }
 protected abstract bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
         BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action);
        // Return value is whether or not we added a new segment.
        bool AddSegment(Point start, Point end, Obstacle eventObstacle
                                , BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide
                                , SweepEvent action, double weight) {
            DevTraceInfoVgGen(1, "Adding Segment [{0} -> {1} {2}] weight {3}", start, end, weight);
            DevTraceInfoVgGen(2, "     side {0}", lowNborSide);
            DevTraceInfoVgGen(2, "  -> side {0}", highNborSide);
            if (PointComparer.Equal(start, end)) {
                return false;
            }

            // See if the new segment subsumes or can be subsumed by the last one.  gbcList may be null.
            PointAndCrossingsList gbcList = CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(start, end);
            bool extendStart, extendEnd;
            bool wasSubsumed = ScanSegment.Subsume(ref hintScanSegment, start, end, weight, gbcList, ScanDirection
                                        , ParallelScanSegments, out extendStart, out extendEnd);
            if (!wasSubsumed) {
                Debug.Assert((weight != ScanSegment.ReflectionWeight) || (ParallelScanSegments.Find(start, end) == null),
                            "Reflection segments already in the ScanSegmentTree should should have been detected before calling AddSegment");
                hintScanSegment = ParallelScanSegments.InsertUnique(new ScanSegment(start, end, weight, gbcList)).Item;
            } else if (weight == ScanSegment.ReflectionWeight) {
                // Do not continue this; it is probably a situation where a side is at a tiny angle from the axis,
                // resulting in an initial reflection segment that is parallel and very close to the extreme-vertex-derived
                // segment, so as the staircase progresses they eventually converge due to floating-point rounding.
                // See RectilinearFilesTest.ReflectionStaircasesConverge.
                return false;
            }

            // Do reflections only if the new segment is not overlapped.
            if (ScanSegment.OverlappedWeight != weight) {
                // If these fire, it's probably an indication that isOverlapped is not correctly set
                // and one of the neighbors is an OverlapSide from CreateScanSegments.
                Debug.Assert(lowNborSide is HighObstacleSide, "lowNbor is not HighObstacleSide");
                Debug.Assert(highNborSide is LowObstacleSide, "highNbor is not LowObstacleSide");

                // If we are closing the obstacle then the initial Obstacles of the reflections (the ones it
                // will bounce between) are the opposite neighbors.  Otherwise, the OpenVertexEvent obstacle
                // is the ReflectionEvent initial obstacle.
                if (action is CloseVertexEvent) {
                    // If both neighbor sides reflect upward, they can't intersect, so we don't need
                    // to store a lookahead site (if neither reflect upward, StoreLookaheadSite no-ops).
                    if (!SideReflectsUpward(lowNborSide) || !SideReflectsUpward(highNborSide)) {
                        // Try to store both; only one will "take" (for the upward-reflecting side).
                        // The initial Obstacle is the opposite neighbor.
                        if (extendStart) {
                            this.StoreLookaheadSite(highNborSide.Obstacle, lowNborSide, start, wantExtreme:false);
                        }
                        if (extendEnd) {
                            this.StoreLookaheadSite(lowNborSide.Obstacle, highNborSide, end, wantExtreme: false);
                        }
                    }
                }
                else {
                    if (extendStart) {
                        StoreLookaheadSite(eventObstacle, LowNeighborSides.GroupSideInterveningBeforeLowNeighbor, lowNborSide, start);
                    }
                    if (extendEnd) {
                        StoreLookaheadSite(eventObstacle, HighNeighborSides.GroupSideInterveningBeforeHighNeighbor, highNborSide, end);
                    }
                }
            }

            DevTraceInfoVgGen(2, "HintScanSegment {0}{1}", hintScanSegment, wasSubsumed ? " (subsumed)" : "");
            DevTrace_DumpScanSegmentsDuringAdd(3);
            return true;
        }
 internal GroupBoundaryCrossing(Obstacle group, Directions dirToInside)
 {
     Debug.Assert(CompassVector.IsPureDirection(dirToInside), "Impure direction");
     this.Group             = group;
     this.DirectionToInside = dirToInside;
 }
 internal OpenVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
 // If true, we have a staircase situation.
 internal bool IsStaircaseStep(Obstacle reflectionTarget)
 {
     return(this.InitialObstacle == reflectionTarget);
 }
        private void AddGroupIntersectionsToRestrictedRay(Obstacle obstacle, IList<IntersectionInfo> intersections) {
            // We'll let the lines punch through any intersections with groups, but track the location so we can enable/disable crossing.
            foreach (var intersectionInfo in intersections) {
                var intersect = SpliceUtility.RawIntersection(intersectionInfo, currentRestrictedRay.Start);

                // Skip intersections that are past the end of the restricted segment (though there may still be some
                // there if we shorten it later, but we'll skip them later).
                var distSquared = (intersect - currentRestrictedRay.Start).LengthSquared;
                if (distSquared > restrictedRayLengthSquared) {
                    continue;
                }

                var dirTowardIntersect = PointComparer.GetPureDirection(currentRestrictedRay.Start, currentRestrictedRay.End);
                var polyline = (Polyline)intersectionInfo.Segment1; // this is the second arg to GetAllIntersections
                var dirsOfSide = CompassVector.VectorDirection(polyline.Derivative(intersectionInfo.Par1));

                // The derivative is always clockwise, so if the side contains the rightward rotation of the
                // direction from the ray origin, then we're hitting it from the inside; otherwise from the outside.
                var dirToInsideOfGroup = dirTowardIntersect;
                if (0 != (dirsOfSide & CompassVector.RotateRight(dirTowardIntersect))) {
                    dirToInsideOfGroup = CompassVector.OppositeDir(dirToInsideOfGroup);
                }
                CurrentGroupBoundaryCrossingMap.AddIntersection(intersect, obstacle, dirToInsideOfGroup);
            }
        }