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);
        }
        internal VisibilityEdge FindOrAddEdge(VisibilityVertex sourceVertex, VisibilityVertex targetVertex, double weight)
        {
            // Since we're adding transient edges into the graph, we're not doing full intersection
            // evaluation; thus there may already be an edge from the source vertex in the direction
            // of the target vertex, but ending before or after the target vertex.
            Directions       dirToTarget = PointComparer.GetPureDirection(sourceVertex, targetVertex);
            VisibilityVertex bracketSource, bracketTarget;

            // Is there an edge in the chain from sourceVertex in the direction of targetVertex
            // that brackets targetvertex?
            //      <sourceVertex> -> ..1.. -> ..2.. <end>   3
            // Yes if targetVertex is at the x above 1 or 2, No if it is at 3.  If false, bracketSource
            // will be set to the vertex at <end> (if there are any edges in that direction at all).
            VisibilityVertex splitVertex = targetVertex;

            if (!FindBracketingVertices(sourceVertex, targetVertex.Point, dirToTarget
                                        , out bracketSource, out bracketTarget))
            {
                // No bracketing of targetVertex from sourceVertex but bracketSource has been updated.
                // Is there a bracket of bracketSource from the targetVertex direction?
                //                      3   <end> ..2.. <- ..1..   <targetVertex>
                // Yes if bracketSource is at the x above 1 or 2, No if it is at 3.  If false, bracketTarget
                // will be set to the vertex at <end> (if there are any edges in that direction at all).
                // If true, then bracketSource and splitVertex must be updated.
                VisibilityVertex tempSource;
                if (FindBracketingVertices(targetVertex, sourceVertex.Point, CompassVector.OppositeDir(dirToTarget)
                                           , out bracketTarget, out tempSource))
                {
                    Debug.Assert(bracketSource == sourceVertex, "Mismatched bracketing detection");
                    bracketSource = tempSource;
                    splitVertex   = sourceVertex;
                }
            }

            // If null != edge then targetVertex is between bracketSource and bracketTarget and SplitEdge returns the
            // first half-edge (and weight is ignored as the split uses the edge weight).
            var edge = VisGraph.FindEdge(bracketSource.Point, bracketTarget.Point);

            edge = (null != edge)
                    ? this.SplitEdge(edge, splitVertex)
                    : CreateEdge(bracketSource, bracketTarget, weight);
            DevTrace_VerifyEdge(edge);
            return(edge);
        }
        private void SpliceGroupBoundaryCrossing(VisibilityVertex currentVertex, PointAndCrossings pac, Directions dirToInside)
        {
            GroupBoundaryCrossing[] crossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, dirToInside);
            if (null != crossings)
            {
                var outerVertex = VisGraph.FindVertex(pac.Location) ?? AddVertex(pac.Location);
                if (currentVertex.Point != outerVertex.Point)
                {
                    FindOrAddEdge(currentVertex, outerVertex);
                }
                var interiorPoint  = crossings[0].GetInteriorVertexPoint(pac.Location);
                var interiorVertex = VisGraph.FindVertex(interiorPoint) ?? AddVertex(interiorPoint);

                // FindOrAddEdge splits an existing edge so may not return the portion bracketed by outerVertex and interiorVertex.
                FindOrAddEdge(outerVertex, interiorVertex);
                var edge           = VisGraph.FindEdge(outerVertex.Point, interiorVertex.Point);
                var crossingsArray = crossings.Select(c => c.Group.InputShape).ToArray();
                edge.IsPassable = delegate { return(crossingsArray.Any(s => s.IsTransparent)); };
            }
        }
        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);
        }
        internal VisibilityEdge FindOrAddEdge(VisibilityVertex sourceVertex, VisibilityVertex targetVertex, double weight)
        {
            // Since we're adding transient edges into the graph, we're not doing full intersection
            // evaluation; thus there may already be an edge from the source vertex in the direction
            // of the target vertex, but ending before or after the target vertex.
            Direction        dirToTarget = PointComparer.GetPureDirection(sourceVertex, targetVertex);
            VisibilityVertex bracketSource, bracketTarget, splitVertex;

            GetBrackets(sourceVertex, targetVertex, dirToTarget, out bracketSource, out bracketTarget, out splitVertex);

            // If null != edge then targetVertex is between bracketSource and bracketTarget and SplitEdge returns the
            // first half-edge (and weight is ignored as the split uses the edge weight).
            var edge = VisGraph.FindEdge(bracketSource.Point, bracketTarget.Point);

            edge = (edge != null)
                    ? this.SplitEdge(edge, splitVertex)
                    : CreateEdge(bracketSource, bracketTarget, weight);
            DevTrace_VerifyEdge(edge);
            return(edge);
        }
        // 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);
        }
        internal VisibilityVertex FindOrAddVertex(Point location)
        {
            var vertex = VisGraph.FindVertex(location);

            return(vertex ?? AddVertex(location));
        }