internal void ConnectVertexToTargetVertex(VisibilityVertex sourceVertex, VisibilityVertex targetVertex, Directions finalEdgeDir, double weight)
        {
            // finalDir is the required direction of the final edge to the targetIntersect
            // (there will be two edges if we have to add a bend vertex).
            StaticGraphUtility.Assert(PointComparer.IsPureDirection(finalEdgeDir), "finalEdgeDir is not pure", ObstacleTree, VisGraph);

            // targetIntersect may be CenterVertex if that is on an extreme bend or a flat border.
            if (PointComparer.Equal(sourceVertex.Point, targetVertex.Point))
            {
                return;
            }

            // If the target is collinear with sourceVertex we can just create one edge to it.
            Directions targetDirs = PointComparer.GetDirections(sourceVertex.Point, targetVertex.Point);

            if (PointComparer.IsPureDirection(targetDirs))
            {
                FindOrAddEdge(sourceVertex, targetVertex);
                return;
            }

            // Not collinear so we need to create a bend vertex and edge if they don't yet exist.
            Point            bendPoint  = StaticGraphUtility.FindBendPointBetween(sourceVertex.Point, targetVertex.Point, finalEdgeDir);
            VisibilityVertex bendVertex = FindOrAddVertex(bendPoint);

            FindOrAddEdge(sourceVertex, bendVertex, weight);

            // Now create the outer target vertex if it doesn't exist.
            FindOrAddEdge(bendVertex, targetVertex, weight);
        }
        internal VisibilityEdge FindPerpendicularOrContainingEdge(VisibilityVertex startVertex
                                                                  , Directions 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.FindNextVertex(startVertex, dir);
                if (null == nextVertex)
                {
                    break;
                }
                Directions 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);
        }
        private void ExtendEdgeChain(VisibilityVertex startVertex, Directions 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.
            Directions 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.
            Directions       spliceSourceDir = CompassVector.RotateLeft(extendDir);
            VisibilityVertex spliceSource    = StaticGraphUtility.FindNextVertex(startVertex, spliceSourceDir);

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

            // Store this off before ExtendSpliceWorker, which overwrites it.
            Directions       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);
        }
        internal VisibilityEdge SplitEdge(VisibilityEdge edge, VisibilityVertex splitVertex)
        {
            // If the edge is NULL it means we could not find an appropriate one, so do nothing.
            if (null == edge)
            {
                return(null);
            }

            StaticGraphUtility.Assert(StaticGraphUtility.PointIsOnSegment(edge.SourcePoint, edge.TargetPoint, splitVertex.Point)
                                      , "splitVertex is not on edge", ObstacleTree, VisGraph);
            if (PointComparer.Equal(edge.Source.Point, splitVertex.Point) || PointComparer.Equal(edge.Target.Point, splitVertex.Point))
            {
                // No split needed.
                return(edge);
            }

            // Store the original edge, if needed.
            if (!(edge is TollFreeVisibilityEdge))
            {
                edgesToRestore.Add(edge);
            }

            VisibilityGraph.RemoveEdge(edge);

            // If this is an overlapped edge, or we're in sparseVg, then it may be an unpadded->padded edge that crosses
            // over another obstacle's padded boundary, and then either a collinear splice from a free point or another
            // obstacle in the same cluster starts splicing from that leapfrogged boundary, so we have the edges:
            //      A   ->   D                      | D is unpadded, A is padded border of sourceObstacle
            //        B -> C  ->  E  ->  F          | B and C are vertical ScanSegments between A and D
            //      <-- splice direction is West    | F is unpadded, E is padded border of targetObstacle
            // Now after splicing F to E to C to B we go A, calling FindOrAddEdge B->A; the bracketing process finds
            // A->D which we'll be splitting at B, which would wind up with A->B, B->C, B->D, having to Eastward
            // outEdges from B.  See RectilinearTests.Reflection_Block1_Big_UseRect for overlapped, and
            // RectilinearTests.FreePortLocationRelativeToTransientVisibilityEdgesSparseVg for sparseVg.
            // To avoid this we add the edges in each direction from splitVertex with FindOrAddEdge.  If we've
            // come here from a previous call to FindOrAddEdge, then that call has found the bracketing vertices,
            // which are the endpoints of 'edge', and we've removed 'edge', so we will not call SplitEdge again.
            if ((this.IsSparseVg || (edge.Weight == ScanSegment.OverlappedWeight)) && (splitVertex.Degree > 0))
            {
                FindOrAddEdge(splitVertex, edge.Source, edge.Weight);
                return(FindOrAddEdge(splitVertex, edge.Target, edge.Weight));
            }

            // Splice it into the graph in place of targetEdge.  Return the first half, because
            // this may be called from AddEdge, in which case the split vertex is the target vertex.
            CreateEdge(splitVertex, edge.Target, edge.Weight);
            return(CreateEdge(edge.Source, splitVertex, edge.Weight));
        }
        internal void ExtendEdgeChain(VisibilityVertex startVertex, Rectangle limitRect, LineSegment maxVisibilitySegment,
                                      PointAndCrossingsList pacList, bool isOverlapped)
        {
            var dir = PointComparer.GetDirections(maxVisibilitySegment.Start, maxVisibilitySegment.End);

            if (dir == Directions.None)
            {
                return;
            }

            Debug.Assert(CompassVector.IsPureDirection(dir), "impure max visibility segment");

            // Shoot the edge chain out to the shorter of max visibility or intersection with the limitrect.
            StaticGraphUtility.Assert(PointComparer.Equal(maxVisibilitySegment.Start, startVertex.Point) ||
                                      (PointComparer.GetPureDirection(maxVisibilitySegment.Start, startVertex.Point) == dir)
                                      , "Inconsistent direction found", ObstacleTree, VisGraph);
            double oppositeFarBound      = StaticGraphUtility.GetRectangleBound(limitRect, dir);
            Point  maxDesiredSplicePoint = StaticGraphUtility.IsVertical(dir)
                                    ? ApproximateComparer.Round(new Point(startVertex.Point.X, oppositeFarBound))
                                    : ApproximateComparer.Round(new Point(oppositeFarBound, startVertex.Point.Y));

            if (PointComparer.Equal(maxDesiredSplicePoint, startVertex.Point))
            {
                // Nothing to do.
                return;
            }
            if (PointComparer.GetPureDirection(startVertex.Point, maxDesiredSplicePoint) != dir)
            {
                // It's in the opposite direction, so no need to do anything.
                return;
            }

            // If maxDesiredSplicePoint is shorter, create a new shorter segment.  We have to pass both segments
            // through to the worker function so it knows whether it can go past maxDesiredSegment (which may be limited
            // by limitRect).
            var maxDesiredSegment = maxVisibilitySegment;

            if (PointComparer.GetDirections(maxDesiredSplicePoint, maxDesiredSegment.End) == dir)
            {
                maxDesiredSegment = new LineSegment(maxDesiredSegment.Start, maxDesiredSplicePoint);
            }

            ExtendEdgeChain(startVertex, dir, maxDesiredSegment, maxVisibilitySegment, pacList, isOverlapped);
        }
        internal VisibilityVertex AddEdgeToTargetEdge(VisibilityVertex sourceVertex, VisibilityEdge targetEdge
                                                      , Point targetIntersect)
        {
            StaticGraphUtility.Assert(PointComparer.Equal(sourceVertex.Point, targetIntersect) ||
                                      PointComparer.IsPureDirection(sourceVertex.Point, targetIntersect)
                                      , "non-orthogonal edge request", ObstacleTree, VisGraph);
            StaticGraphUtility.Assert(StaticGraphUtility.PointIsOnSegment(targetEdge.SourcePoint, targetEdge.TargetPoint, targetIntersect)
                                      , "targetIntersect is not on targetEdge", ObstacleTree, VisGraph);

            // If the target vertex does not exist, we must split targetEdge to add it.
            VisibilityVertex targetVertex = VisGraph.FindVertex(targetIntersect);

            if (null == targetVertex)
            {
                targetVertex = AddVertex(targetIntersect);
                SplitEdge(targetEdge, targetVertex);
            }
            FindOrAddEdge(sourceVertex, targetVertex);
            return(targetVertex);
        }
        private void Debug_VerifyNonOverlappedExtension(bool isOverlapped, VisibilityVertex extendVertex, VisibilityVertex nextExtendVertex,
                                                        VisibilityVertex spliceSource, VisibilityVertex spliceTarget)
        {
            if (isOverlapped)
            {
                return;
            }
#if TEST_MSAGL
            StaticGraphUtility.Assert(!this.ObstacleTree.SegmentCrossesANonGroupObstacle(extendVertex.Point, nextExtendVertex.Point)
                                      , "extendDir edge crosses an obstacle", this.ObstacleTree, this.VisGraph);
#endif // TEST_MSAGL

            if (spliceSource == null)
            {
                // Only verifying the direct extension.
                return;
            }

            // Verify lateral splices as well.
            if ((null == spliceTarget) ||
                (null == this.VisGraph.FindEdge(spliceSource.Point, spliceTarget.Point) &&
                 (null == this.VisGraph.FindEdge(spliceSource.Point, nextExtendVertex.Point))))
            {
                // If targetVertex isn't null and the proposed edge from nextExtendVertex -> targetVertex
                // edge doesn't already exist, then we assert that we're not creating a new edge that
                // crosses the obstacle bounds (a bounds-crossing edge may already exist, from a port
                // within the obstacle; in that case nextExtendPoint splits that edge).  As above, don't
                // splice laterally across groups.
                StaticGraphUtility.Assert(!this.ObstacleTree.SegmentCrossesAnObstacle(spliceSource.Point, nextExtendVertex.Point)
                                          , "spliceSource->extendVertex edge crosses an obstacle", this.ObstacleTree, this.VisGraph);

                // Above we moved spliceTarget over when nextExtendVertex existed, so account
                // for that here.
                StaticGraphUtility.Assert((null == spliceTarget) ||
                                          (null != this.VisGraph.FindEdge(nextExtendVertex.Point, spliceTarget.Point)) ||
                                          !this.ObstacleTree.SegmentCrossesAnObstacle(nextExtendVertex.Point, spliceTarget.Point)
                                          , "extendVertex->spliceTarget edge crosses an obstacle", this.ObstacleTree, this.VisGraph);
            }
        }
Exemple #8
0
        internal void ExtendEdgeChain(TransientGraphUtility transUtil, VisibilityVertex targetVertex, Directions dirToExtend, Rectangle limitRect)
        {
            // Extend the edge chain to the opposite side of the limit rectangle.
            StaticGraphUtility.Assert(PointComparer.Equal(this.Point, targetVertex.Point) ||
                                      (PointComparer.GetPureDirection(this.Point, targetVertex.Point) == dirToExtend)
                                      , "input dir does not match with to-targetVertex direction", transUtil.ObstacleTree, transUtil.VisGraph);

            var extendOverlapped = IsOverlapped;

            if (extendOverlapped)
            {
                // The initial vertex we connected to may be on the border of the enclosing obstacle,
                // or of another also-overlapped obstacle.  If the former, we turn off overlap now.
                extendOverlapped = transUtil.ObstacleTree.PointIsInsideAnObstacle(targetVertex.Point, dirToExtend);
            }

            // If we're inside an obstacle's boundaries we'll never extend past the end of the obstacle
            // due to encountering the boundary from the inside.  So start the extension at targetVertex.
            SegmentAndCrossings segmentAndCrossings = GetSegmentAndCrossings(this.IsOverlapped ? targetVertex : this.Vertex, dirToExtend, transUtil);

            transUtil.ExtendEdgeChain(targetVertex, limitRect, segmentAndCrossings.Item1, segmentAndCrossings.Item2, extendOverlapped);
        }
        // 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, Directions extendDir, Directions 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.FindNextVertex(spliceSource, spliceTargetDir);

            spliceTarget = StaticGraphUtility.FindNextVertex(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 (null == spliceTarget)
                {
                    // 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 (null != nextExtendVertex)
                {
                    if ((null == spliceTarget) || (null != this.VisGraph.FindEdge(extendVertex.Point, nextExtendPoint)))
                    {
                        // 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 (null == spliceTarget)
                        {
                            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.FindNextVertex(nextExtendVertex, spliceTargetDir)
                                              , "no edge exists between an existing nextExtendVertex and spliceTarget"
                                              , ObstacleTree, VisGraph);
                }
                else
                {
                    StaticGraphUtility.Assert((null == spliceTarget) ||
                                              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(null != spliceTarget);
        }