private static bool GetNextSpliceSource(ref VisibilityVertex spliceSource, Direction spliceTargetDir, Direction extendDir)
        {
            VisibilityVertex nextSpliceSource = StaticGraphUtility.FindAdjacentVertex(spliceSource, extendDir);

            if (nextSpliceSource == null)
            {
                // See if there is a source further away from the extension line - we might have
                // been on freePoint line (or another nearby PortEntry line) that dead-ended.
                // Look laterally from the previous spliceSource first.
                nextSpliceSource = spliceSource;
                for (;;)
                {
                    nextSpliceSource = StaticGraphUtility.FindAdjacentVertex(nextSpliceSource, CompassVector.OppositeDir(spliceTargetDir));
                    if (nextSpliceSource == null)
                    {
                        return(false);
                    }
                    var nextSpliceSourceExtend = StaticGraphUtility.FindAdjacentVertex(nextSpliceSource, extendDir);
                    if (nextSpliceSourceExtend != null)
                    {
                        nextSpliceSource = nextSpliceSourceExtend;
                        break;
                    }
                }
            }
            spliceSource = nextSpliceSource;
            return(true);
        }
        private static VisibilityVertex GetSpliceTarget(ref VisibilityVertex spliceSource, Direction spliceTargetDir, Point nextExtendPoint)
        {
            // Look for the target.  There may be a dead-ended edge starting at the current spliceSource
            // edge that has a vertex closer to the extension line; in that case keep walking until we
            // have the closest vertex on the Source side of the extension line as spliceSource.
            Direction prevDir      = PointComparer.GetPureDirection(spliceSource.Point, nextExtendPoint);
            Direction nextDir      = prevDir;
            var       spliceTarget = spliceSource;

            while (nextDir == prevDir)
            {
                spliceSource = spliceTarget;
                spliceTarget = StaticGraphUtility.FindAdjacentVertex(spliceSource, spliceTargetDir);
                if (spliceTarget == null)
                {
                    break;
                }
                if (PointComparer.Equal(spliceTarget.Point, nextExtendPoint))
                {
                    // If we encountered an existing vertex for the extension chain, update spliceTarget
                    // to be after it and we're done with this loop.
                    spliceTarget = StaticGraphUtility.FindAdjacentVertex(spliceTarget, spliceTargetDir);
                    break;
                }
                nextDir = PointComparer.GetPureDirection(spliceTarget.Point, nextExtendPoint);
            }
            return(spliceTarget);
        }
        internal VisibilityEdge FindPerpendicularOrContainingEdge(VisibilityVertex startVertex
                                                                  , Direction dir, Point pointLocation)
        {
            // Return the edge in 'dir' from startVertex that is perpendicular to pointLocation.
            // startVertex must therefore be located such that pointLocation is in 'dir' direction from it,
            // or is on the same line.
            StaticGraphUtility.Assert(0 == (CompassVector.OppositeDir(dir)
                                            & PointComparer.GetDirections(startVertex.Point, pointLocation))
                                      , "the ray from 'dir' is away from pointLocation", ObstacleTree, VisGraph);
            while (true)
            {
                VisibilityVertex nextVertex = StaticGraphUtility.FindAdjacentVertex(startVertex, dir);
                if (nextVertex == null)
                {
                    break;
                }
                Direction dirCheck = PointComparer.GetDirections(nextVertex.Point, pointLocation);

                // If the next vertex is past the intersection with pointLocation, this edge brackets it.
                if (0 != (CompassVector.OppositeDir(dir) & dirCheck))
                {
                    return(VisGraph.FindEdge(startVertex.Point, nextVertex.Point));
                }
                startVertex = nextVertex;
            }
            return(null);
        }
 internal static bool FindBracketingVertices(VisibilityVertex sourceVertex, Point targetPoint, Direction dirToTarget
                                             , out VisibilityVertex bracketSource, out VisibilityVertex bracketTarget)
 {
     // Walk from the source to target until we bracket target or there is no nextVertex
     // in the desired direction.
     bracketSource = sourceVertex;
     for (; ;)
     {
         bracketTarget = StaticGraphUtility.FindAdjacentVertex(bracketSource, dirToTarget);
         if (bracketTarget == null)
         {
             break;
         }
         if (PointComparer.Equal(bracketTarget.Point, targetPoint))
         {
             // Desired edge already exists.
             return(true);
         }
         if (dirToTarget != PointComparer.GetPureDirection(bracketTarget.Point, targetPoint))
         {
             // bracketTarget is past vertex in the traversal direction.
             break;
         }
         bracketSource = bracketTarget;
     }
     return(bracketTarget != null);
 }
Example #5
0
        private VisibilityEdge AddVisibilityEdge(VisibilityVertex source, VisibilityVertex target)
        {
            Debug.Assert(source.Point != target.Point, "Self-edges are not allowed");
            Debug.Assert(PointComparer.IsPureLower(source.Point, target.Point), "Impure or reversed direction encountered");

            // Make sure we aren't adding two edges in the same direction to the same vertex.
            Debug.Assert(null == StaticGraphUtility.FindAdjacentVertex(source, StaticGraphUtility.EdgeDirection(source, target))
                         , "Duplicate outEdge from Source vertex");
            Debug.Assert(null == StaticGraphUtility.FindAdjacentVertex(target, StaticGraphUtility.EdgeDirection(target, source))
                         , "Duplicate inEdge to Target vertex");
            var edge = new VisibilityEdge(source, target, this.Weight);

            VisibilityGraph.AddEdge(edge);
            return(edge);
        }
        private void ExtendEdgeChain(VisibilityVertex startVertex, Direction extendDir
                                     , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment
                                     , PointAndCrossingsList pacList, bool isOverlapped)
        {
            StaticGraphUtility.Assert(PointComparer.GetPureDirection(maxDesiredSegment.Start, maxDesiredSegment.End) == extendDir
                                      , "maxDesiredSegment is reversed", ObstacleTree, VisGraph);

            // Direction*s*, because it may return None, which is valid and means startVertex is on the
            // border of an obstacle and we don't want to go inside it.
            Direction segmentDir = PointComparer.GetDirections(startVertex.Point, maxDesiredSegment.End);

            if (segmentDir != extendDir)
            {
                // OppositeDir may happen on overlaps where the boundary has a gap in its ScanSegments due to other obstacles
                // overlapping it and each other.  This works because the port has an edge connected to startVertex,
                // which is on a ScanSegment outside the obstacle.
                StaticGraphUtility.Assert(isOverlapped || (segmentDir != CompassVector.OppositeDir(extendDir))
                                          , "obstacle encountered between prevPoint and startVertex", ObstacleTree, VisGraph);
                return;
            }

            // We'll find the segment to the left (or right if to the left doesn't exist),
            // then splice across in the opposite direction.
            Direction        spliceSourceDir = CompassVector.RotateLeft(extendDir);
            VisibilityVertex spliceSource    = StaticGraphUtility.FindAdjacentVertex(startVertex, spliceSourceDir);

            if (spliceSource == null)
            {
                spliceSourceDir = CompassVector.OppositeDir(spliceSourceDir);
                spliceSource    = StaticGraphUtility.FindAdjacentVertex(startVertex, spliceSourceDir);
                if (spliceSource == null)
                {
                    return;
                }
            }

            // Store this off before ExtendSpliceWorker, which overwrites it.
            Direction        spliceTargetDir = CompassVector.OppositeDir(spliceSourceDir);
            VisibilityVertex spliceTarget;

            if (ExtendSpliceWorker(spliceSource, extendDir, spliceTargetDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget))
            {
                // We ended on the source side and may have dead-ends on the target side so reverse sides.
                ExtendSpliceWorker(spliceTarget, extendDir, spliceSourceDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget);
            }

            SpliceGroupBoundaryCrossings(pacList, startVertex, maxDesiredSegment);
        }
        private static VisibilityVertex TraverseToFirstVertexAtOrAbove(VisibilityVertex startVertex, Point start, Direction dir)
        {
            var returnVertex = startVertex;
            var oppositeDir  = CompassVector.OppositeDir(dir);

            for ( ; ;)
            {
                var nextVertex = StaticGraphUtility.FindAdjacentVertex(returnVertex, dir);

                // This returns Directions. None on a match.
                if ((nextVertex == null) || (PointComparer.GetDirections(nextVertex.Point, start) == oppositeDir))
                {
                    break;
                }
                returnVertex = nextVertex;
            }
            return(returnVertex);
        }
        internal VisibilityEdge FindNearestPerpendicularOrContainingEdge(VisibilityVertex startVertex
                                                                         , Direction dir, Point pointLocation)
        {
            // Similar to FindPerpendicularEdge, but first try to move closer to pointLocation,
            // as long as there are edges going in 'dir' that extend to pointLocation.
            Direction dirTowardLocation = ~dir& PointComparer.GetDirections(startVertex.Point, pointLocation);

            // If Directions. None then pointLocation is collinear.
            VisibilityVertex currentVertex            = startVertex;
            Direction        currentDirTowardLocation = dirTowardLocation;

            // First move toward pointLocation far as we can.
            while (Direction.None != currentDirTowardLocation)
            {
                VisibilityVertex nextVertex = StaticGraphUtility.FindAdjacentVertex(currentVertex, dirTowardLocation);
                if (nextVertex == null)
                {
                    break;
                }
                if (0 != (CompassVector.OppositeDir(dirTowardLocation)
                          & PointComparer.GetDirections(nextVertex.Point, pointLocation)))
                {
                    break;
                }
                currentVertex            = nextVertex;
                currentDirTowardLocation = ~dir& PointComparer.GetDirections(currentVertex.Point, pointLocation);
            }

            // Now find the first vertex that has a chain that intersects pointLocation, if any, moving away
            // from pointLocation until we find it or arrive back at startVertex.
            VisibilityEdge perpEdge;

            while (true)
            {
                perpEdge = FindPerpendicularOrContainingEdge(currentVertex, dir, pointLocation);
                if ((perpEdge != null) || (currentVertex == startVertex))
                {
                    break;
                }
                currentVertex = StaticGraphUtility.FindAdjacentVertex(currentVertex, CompassVector.OppositeDir(dirTowardLocation));
            }
            return(perpEdge);
        }
        // The return value is whether we should try a second pass if this is called on the first pass,
        // using spliceTarget to wrap up dead-ends on the target side.
        bool ExtendSpliceWorker(VisibilityVertex spliceSource, Direction extendDir, Direction spliceTargetDir
                                , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment
                                , bool isOverlapped, out VisibilityVertex spliceTarget)
        {
            // This is called after having created at least one extension vertex (initially, the
            // first one added outside the obstacle), so we know extendVertex will be there. spliceSource
            // is the vertex to the OppositeDir(spliceTargetDir) of that extendVertex.
            VisibilityVertex extendVertex = StaticGraphUtility.FindAdjacentVertex(spliceSource, spliceTargetDir);

            spliceTarget = StaticGraphUtility.FindAdjacentVertex(extendVertex, spliceTargetDir);
            for (; ;)
            {
                if (!GetNextSpliceSource(ref spliceSource, spliceTargetDir, extendDir))
                {
                    break;
                }

                // spliceSource is now on the correct edge relative to the desired nextExtendPoint.
                // spliceTarget is in the opposite direction of the extension-line-to-spliceSource.
                Point nextExtendPoint = StaticGraphUtility.FindBendPointBetween(extendVertex.Point
                                                                                , spliceSource.Point, CompassVector.OppositeDir(spliceTargetDir));

                // We test below for being on or past maxDesiredSegment; here we may be skipping
                // over maxDesiredSegmentEnd which is valid since we want to be sure to go to or
                // past limitRect, but be sure to stay within maxVisibilitySegment.
                if (IsPointPastSegmentEnd(maxVisibilitySegment, nextExtendPoint))
                {
                    break;
                }

                spliceTarget = GetSpliceTarget(ref spliceSource, spliceTargetDir, nextExtendPoint);

                //StaticGraphUtility.Test_DumpVisibilityGraph(ObstacleTree, VisGraph);

                if (spliceTarget == null)
                {
                    // This may be because spliceSource was created just for Group boundaries.  If so,
                    // skip to the next nextExtendVertex location.
                    if (this.IsSkippableSpliceSourceWithNullSpliceTarget(spliceSource, extendDir))
                    {
                        continue;
                    }

                    // We're at a dead-end extending from the source side, or there is an intervening obstacle, or both.
                    // Don't splice across lateral group boundaries.
                    if (ObstacleTree.SegmentCrossesAnObstacle(spliceSource.Point, nextExtendPoint))
                    {
                        return(false);
                    }
                }

                // We might be walking through a point where a previous chain dead-ended.
                VisibilityVertex nextExtendVertex = VisGraph.FindVertex(nextExtendPoint);
                if (nextExtendVertex != null)
                {
                    if ((spliceTarget == null) || (this.VisGraph.FindEdge(extendVertex.Point, nextExtendPoint) != null))
                    {
                        // We are probably along a ScanSegment so visibility in this direction has already been determined.
                        // Stop and don't try to continue extension from the opposite side.  If we continue splicing here
                        // it might go across an obstacle.
                        if (spliceTarget == null)
                        {
                            Debug_VerifyNonOverlappedExtension(isOverlapped, extendVertex, nextExtendVertex, spliceSource: null, spliceTarget: null);
                            FindOrAddEdge(extendVertex, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight);
                        }
                        return(false);
                    }

                    // This should always have been found in the find-the-next-target loop above if there is
                    // a vertex (which would be nextExtendVertex, which we just found) between spliceSource
                    // and spliceTarget.  Even for a sparse graph, an edge should not skip over a vertex.
                    StaticGraphUtility.Assert(spliceTarget == StaticGraphUtility.FindAdjacentVertex(nextExtendVertex, spliceTargetDir)
                                              , "no edge exists between an existing nextExtendVertex and spliceTarget"
                                              , ObstacleTree, VisGraph);
                }
                else
                {
                    StaticGraphUtility.Assert((spliceTarget == null) ||
                                              spliceTargetDir == PointComparer.GetPureDirection(nextExtendPoint, spliceTarget.Point)
                                              , "spliceTarget is not to spliceTargetDir of nextExtendVertex"
                                              , ObstacleTree, VisGraph);
                    nextExtendVertex = this.AddVertex(nextExtendPoint);
                }
                FindOrAddEdge(extendVertex, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight);

                Debug_VerifyNonOverlappedExtension(isOverlapped, extendVertex, nextExtendVertex, spliceSource, spliceTarget);

                // This will split the edge if targetVertex is non-null; otherwise we are at a dead-end
                // on the target side so must not create a vertex as it would be inside an obstacle.
                FindOrAddEdge(spliceSource, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight);
                if (isOverlapped)
                {
                    isOverlapped = this.SeeIfSpliceIsStillOverlapped(extendDir, nextExtendVertex);
                }
                extendVertex = nextExtendVertex;

                // Test GetDirections because it may return Directions. None.
                if (0 == (extendDir & PointComparer.GetDirections(nextExtendPoint, maxDesiredSegment.End)))
                {
                    // At or past the desired max extension point, so we're done.
                    spliceTarget = null;
                    break;
                }
            }
            return(spliceTarget != null);
        }
        private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment)
        {
            if ((crossingList == null) || (0 == crossingList.Count))
            {
                return;
            }
            crossingList.Reset();
            var start = maxSegment.Start;
            var end   = maxSegment.End;
            var dir   = PointComparer.GetPureDirection(start, end);

            // Make sure we are going in the ascending direction.
            if (!StaticGraphUtility.IsAscending(dir))
            {
                start = maxSegment.End;
                end   = maxSegment.Start;
                dir   = CompassVector.OppositeDir(dir);
            }

            // We need to back up to handle group crossings that are between a VisibilityBorderIntersect on a sloped border and the
            // incoming startVertex (which is on the first ScanSegment in Perpendicular(dir) that is outside that padded border).
            startVertex = TraverseToFirstVertexAtOrAbove(startVertex, start, CompassVector.OppositeDir(dir));

            // Splice into the Vertices between and including the start/end points.
            for (var currentVertex = startVertex; currentVertex != null; currentVertex = StaticGraphUtility.FindAdjacentVertex(currentVertex, dir))
            {
                bool isFinalVertex = (PointComparer.Compare(currentVertex.Point, end) >= 0);
                while (crossingList.CurrentIsBeforeOrAt(currentVertex.Point))
                {
                    PointAndCrossings pac = crossingList.Pop();

                    // If it's past the start and at or before the end, splice in the crossings in the descending direction.
                    if (PointComparer.Compare(pac.Location, startVertex.Point) > 0)
                    {
                        if (PointComparer.Compare(pac.Location, end) <= 0)
                        {
                            SpliceGroupBoundaryCrossing(currentVertex, pac, CompassVector.OppositeDir(dir));
                        }
                    }

                    // If it's at or past the start and before the end, splice in the crossings in the descending direction.
                    if (PointComparer.Compare(pac.Location, startVertex.Point) >= 0)
                    {
                        if (PointComparer.Compare(pac.Location, end) < 0)
                        {
                            SpliceGroupBoundaryCrossing(currentVertex, pac, dir);
                        }
                    }
                }

                if (isFinalVertex)
                {
                    break;
                }
            }
        }