private void RecurseRestrictRayWithObstacles(RectangleNode <Obstacle, Point> rectNode)
        {
            // A lineSeg that moves along the boundary of an obstacle is not blocked by it.
            if (!StaticGraphUtility.RectangleInteriorsIntersect(currentRestrictedRay.BoundingBox, (Rectangle)rectNode.Rectangle))
            {
                return;
            }

            Obstacle obstacle = rectNode.UserData;

            if (null != obstacle)
            {
                // Leaf node. Get the interior intersections.  Use the full-length original segment for the intersection calculation.
                IList <IntersectionInfo> intersections = Curve.GetAllIntersections(restrictedIntersectionTestSegment, obstacle.VisibilityPolyline, true /*liftIntersections*/);

                if (!obstacle.IsGroup || stopAtGroups)
                {
                    LookForCloserNonGroupIntersectionToRestrictRay(intersections);
                    return;
                }

                if (wantGroupCrossings)
                {
                    AddGroupIntersectionsToRestrictedRay(obstacle, intersections);
                }

                Debug.Assert(rectNode.IsLeaf, "RectNode with UserData is not a Leaf");
                return;
            }

            // Not a leaf; recurse into children.
            RecurseRestrictRayWithObstacles(rectNode.Left);
            RecurseRestrictRayWithObstacles(rectNode.Right);
        }
Exemplo n.º 2
0
        private bool InteriorEdgeCrossesObstacle(Rectangle rect, Func <Obstacle, Polyline> whichPolylineToUse, IEnumerable <Obstacle> candidates)
        {
            LineSegment lineSeg = null;

            foreach (var blocker in candidates)
            {
                var blockerPolyline = whichPolylineToUse(blocker);
                if (!StaticGraphUtility.RectangleInteriorsIntersect(rect, blockerPolyline.BoundingBox))
                {
                    continue;
                }

                lineSeg = lineSeg ?? new LineSegment(this.UnpaddedBorderIntersect, this.VisibilityBorderIntersect);
                var xx = Curve.CurveCurveIntersectionOne(lineSeg, blockerPolyline, liftIntersection: false);
                if (xx != null)
                {
                    return(true);
                }

                if (PointLocation.Outside != Curve.PointRelativeToCurveLocation(this.UnpaddedBorderIntersect, blockerPolyline))
                {
                    return(true);
                }
            }
            return(false);
        }
        private static bool GetNextSpliceSource(ref VisibilityVertex spliceSource, Directions spliceTargetDir, Directions extendDir)
        {
            VisibilityVertex nextSpliceSource = StaticGraphUtility.FindNextVertex(spliceSource, extendDir);

            if (null == nextSpliceSource)
            {
                // 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.FindNextVertex(nextSpliceSource, CompassVector.OppositeDir(spliceTargetDir));
                    if (null == nextSpliceSource)
                    {
                        return(false);
                    }
                    var nextSpliceSourceExtend = StaticGraphUtility.FindNextVertex(nextSpliceSource, extendDir);
                    if (null != nextSpliceSourceExtend)
                    {
                        nextSpliceSource = nextSpliceSourceExtend;
                        break;
                    }
                }
            }
            spliceSource = nextSpliceSource;
            return(true);
        }
        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 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);
        }
        static internal Point RawIntersection(IntersectionInfo xx, Point origin)
        {
            // If this fires, then you didn't pass the LineSegment as the first argument to GetAllIntersections.
            Debug.Assert(xx.Segment0 is LineSegment, "LineSegment was not first arg to GetAllIntersections");

            // The intersection snaps the end of the intersection to the PolylinePoint at the start/end
            // of the interesecting segment on the obstacle if the intersection is Curve.CloseIntersections
            // to that segment endpoint, which can return a point that is just more than Curve.DistanceEpsilon
            // off the line.  Therefore, re-create the intersection using the LineSegment and intersection
            // parameters (this assumes the LineSegment.End is not Curve.CloseIntersections to the intersection).
            Point point = xx.Segment0[xx.Par0];

#if TEST_MSAGL
            // This may legitimately be rounding-error'd in the same way as xx.IntersectionPoint (and the
            // caller addresses this later).  The purpose of the assert is to verify that the LineSegment
            // interception is not outside the bbox in the perpendicular direction.
            var lineSeg = (LineSegment)xx.Segment0;
            if (StaticGraphUtility.IsVertical(PointComparer.GetDirections(lineSeg.Start, lineSeg.End)))
            {
                Debug.Assert(PointComparer.Equal(point.X, origin.X), "segment0 obstacle intersection is off the vertical line");
            }
            else
            {
                Debug.Assert(PointComparer.Equal(point.Y, origin.Y), "segment0 obstacle intersection is off the horizontal line");
            }
#endif // TEST_MSAGL
            return(ApproximateComparer.Round(point));
        }
        HitTestBehavior InsideObstacleHitTest(Point location, Obstacle obstacle)
        {
            if ((obstacle == insideHitTestIgnoreObstacle1) || (obstacle == insideHitTestIgnoreObstacle2))
            {
                // It's one of the two obstacles we already know about.
                return(HitTestBehavior.Continue);
            }

            if (obstacle.IsGroup)
            {
                // Groups are handled differently from overlaps; we create ScanSegments (overlapped
                // if within a non-group obstacle, else non-overlapped), and turn on/off access across
                // the Group boundary vertices.
                return(HitTestBehavior.Continue);
            }

            if (!StaticGraphUtility.PointIsInRectangleInterior(location, obstacle.VisibilityBoundingBox))
            {
                // The point is on the obstacle boundary, not inside it.
                return(HitTestBehavior.Continue);
            }

            // Note: There are rounding issues using Curve.PointRelativeToCurveLocation at angled
            // obstacle boundaries, hence this function.
            Point high = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location
                                                                     , insideHitTestScanDirection.Direction)
                         + insideHitTestScanDirection.DirectionAsPoint;
            Point low = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location
                                                                    , insideHitTestScanDirection.OppositeDirection)
                        - insideHitTestScanDirection.DirectionAsPoint;
            var testSeg = new LineSegment(low, high);
            IList <IntersectionInfo> xxs = Curve.GetAllIntersections(testSeg, obstacle.VisibilityPolyline, true /*liftIntersections*/);

            // If this is an extreme point it can have one intersection, in which case we're either on the border
            // or outside; if it's a collinear flat boundary, there can be 3 intersections to this point which again
            // means we're on the border (and 3 shouldn't happen anymore with the curve intersection fixes and
            // PointIsInsideRectangle check above).  So the interesting case is that we have 2 intersections.
            if (2 == xxs.Count)
            {
                Point firstInt  = SpliceUtility.RawIntersection(xxs[0], location);
                Point secondInt = SpliceUtility.RawIntersection(xxs[1], location);

                // If we're on either intersection, we're on the border rather than inside.
                if (!PointComparer.Equal(location, firstInt) && !PointComparer.Equal(location, secondInt) &&
                    (location.CompareTo(firstInt) != location.CompareTo(secondInt)))
                {
                    // We're inside.  However, this may be an almost-flat side, in which case rounding
                    // could have reported the intersection with the start or end of the same side and
                    // a point somewhere on the interior of that side.  Therefore if both intersections
                    // are on the same side (integral portion of the parameter), we consider location
                    // to be on the border.  testSeg is always xxs[*].Segment0.
                    Debug.Assert(testSeg == xxs[0].Segment0, "incorrect parameter ordering to GetAllIntersections");
                    if (!ApproximateComparer.Close(Math.Floor(xxs[0].Par1), Math.Floor(xxs[1].Par1)))
                    {
                        return(HitTestBehavior.Stop);
                    }
                }
            }
            return(HitTestBehavior.Continue);
        }
Exemplo n.º 8
0
        internal ObstaclePortEntrance(ObstaclePort oport, Point unpaddedBorderIntersect, Direction outDir, ObstacleTree obstacleTree)
        {
            ObstaclePort            = oport;
            UnpaddedBorderIntersect = unpaddedBorderIntersect;
            OutwardDirection        = outDir;

            // Get the padded intersection.
            var lineSeg = new LineSegment(UnpaddedBorderIntersect, StaticGraphUtility.RectangleBorderIntersect(
                                              oport.Obstacle.VisibilityBoundingBox, UnpaddedBorderIntersect, outDir));
            IList <IntersectionInfo> xxs = Curve.GetAllIntersections(lineSeg, oport.Obstacle.VisibilityPolyline, true /*liftIntersections*/);

            Debug.Assert(1 == xxs.Count, "Expected one intersection");
            this.VisibilityBorderIntersect = ApproximateComparer.Round(SpliceUtility.RawIntersection(xxs[0], UnpaddedBorderIntersect));

            this.MaxVisibilitySegment = obstacleTree.CreateMaxVisibilitySegment(this.VisibilityBorderIntersect,
                                                                                this.OutwardDirection, out this.pointAndCrossingsList);

            // Groups are never in a clump (overlapped) but they may still have their port entrance overlapped.
            if (this.Obstacle.IsOverlapped || (this.Obstacle.IsGroup && !this.Obstacle.IsInConvexHull))
            {
                this.IsOverlapped = obstacleTree.IntersectionIsInsideAnotherObstacle(/*sideObstacle:*/ null, this.Obstacle
                                                                                     , this.VisibilityBorderIntersect, ScanDirection.GetInstance(OutwardDirection));
                if (!this.Obstacle.IsGroup || this.IsOverlapped || this.InteriorEdgeCrossesObstacle(obstacleTree))
                {
                    unpaddedToPaddedBorderWeight = ScanSegment.OverlappedWeight;
                }
            }
            if (this.Obstacle.IsInConvexHull && (unpaddedToPaddedBorderWeight == ScanSegment.NormalWeight))
            {
                SetUnpaddedToPaddedBorderWeightFromHullSiblingOverlaps(obstacleTree);
            }
        }
        private static VisibilityVertex GetSpliceTarget(ref VisibilityVertex spliceSource, Directions 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.
            Directions prevDir      = PointComparer.GetPureDirection(spliceSource.Point, nextExtendPoint);
            Directions nextDir      = prevDir;
            var        spliceTarget = spliceSource;

            while (nextDir == prevDir)
            {
                spliceSource = spliceTarget;
                spliceTarget = StaticGraphUtility.FindNextVertex(spliceSource, spliceTargetDir);
                if (null == spliceTarget)
                {
                    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.FindNextVertex(spliceTarget, spliceTargetDir);
                    break;
                }
                nextDir = PointComparer.GetPureDirection(spliceTarget.Point, nextExtendPoint);
            }
            return(spliceTarget);
        }
Exemplo n.º 10
0
        // Append a vertex before LowestVisibilityVertex or after HighestVisibilityVertex.
        internal void AppendVisibilityVertex(VisibilityGraph vg, VisibilityVertex newVertex)
        {
            Debug.Assert(null != newVertex, "newVertex must not be null");
            Debug.Assert((null == LowestVisibilityVertex) == (null == HighestVisibilityVertex), "Mismatched null Lowest/HighestVisibilityVertex");
            Debug.Assert(StaticGraphUtility.PointIsOnSegment(this, newVertex.Point), "newVertex is out of segment range");
            if (null == HighestVisibilityVertex)
            {
                if (!AddGroupCrossingsBeforeHighestVisibilityVertex(vg, newVertex))
                {
                    SetInitialVisibilityVertex(newVertex);
                }
            }
            else
            {
                // In the event of overlaps where ScanSegments share a Start/End at a border, SegmentIntersector
                // may be appending the same Vertex twice.  If that point is on the border of a group,
                // then we may have just added the border-crossing edge as well.
                if (PointComparer.IsPureLower(newVertex.Point, HighestVisibilityVertex.Point))
                {
                    Debug.Assert(null != vg.FindEdge(newVertex.Point, HighestVisibilityVertex.Point)
                                 , "unexpected low/middle insertion to ScanSegment");
                    return;
                }

                // Add the new edge.  This will always be in the ascending direction.
                if (!AddGroupCrossingsBeforeHighestVisibilityVertex(vg, newVertex))
                {
                    AppendHighestVisibilityVertex(newVertex);
                }
            }
        }
 internal static bool FindBracketingVertices(VisibilityVertex sourceVertex, Point targetPoint, Directions 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.FindNextVertex(bracketSource, dirToTarget);
         if (null == bracketTarget)
         {
             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(null != bracketTarget);
 }
Exemplo n.º 12
0
        // ctor

        internal void Update(Point start, Point end)
        {
            Debug.Assert(PointComparer.Equal(start, end) ||
                         StaticGraphUtility.IsAscending(PointComparer.GetPureDirection(start, end))
                         , "non-ascending segment");
            startPoint = start;
            endPoint   = end;
        }
Exemplo n.º 13
0
        private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment)
        {
            if ((null == crossingList) || (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; null != currentVertex; currentVertex = StaticGraphUtility.FindNextVertex(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;
                }
            }
        }
 // Use the internal static xxxInstance properties to get an instance.
 private ScanDirection(Directions directionAlongScanLine)
 {
     System.Diagnostics.Debug.Assert(StaticGraphUtility.IsAscending(directionAlongScanLine),
                                     "directionAlongScanLine must be ascending");
     Direction            = directionAlongScanLine;
     DirectionAsPoint     = CompassVector.ToPoint(Direction);
     PerpDirection        = (Directions.North == directionAlongScanLine) ? Directions.East : Directions.North;
     PerpDirectionAsPoint = CompassVector.ToPoint(PerpDirection);
     OppositeDirection    = CompassVector.OppositeDir(directionAlongScanLine);
 }
Exemplo n.º 15
0
        internal bool HasGroupCrossingBeforePoint(Point point)
        {
            if (!this.HasGroupCrossings)
            {
                return(false);
            }
            var pac = StaticGraphUtility.IsAscending(this.OutwardDirection) ? this.pointAndCrossingsList.First : this.pointAndCrossingsList.Last;

            return(PointComparer.GetDirections(this.MaxVisibilitySegment.Start, pac.Location) == PointComparer.GetDirections(pac.Location, point));
        }
Exemplo n.º 16
0
 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;
     }
 }
        protected override void ProcessVertexEvent(RBNode <BasicObstacleSide> lowSideNode,
                                                   RBNode <BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent)
        {
            var vertexPoints = (base.ScanDirection.IsHorizontal) ? this.horizontalVertexPoints : this.verticalVertexPoints;

            vertexPoints.Insert(vertexEvent.Site);

            // For easier reading...
            var lowNborSide  = LowNeighborSides.LowNeighbor.Item;
            var highNborSide = HighNeighborSides.HighNeighbor.Item;
            var highDir      = base.ScanDirection.Direction;
            var lowDir       = base.ScanDirection.OppositeDirection;

            // Generate the neighbor side intersections, regardless of overlaps; these are the type-2 Steiner points.
            var lowSteiner  = ScanLineIntersectSide(vertexEvent.Site, lowNborSide);
            var highSteiner = ScanLineIntersectSide(vertexEvent.Site, highNborSide);

            // Add the intersections at the neighbor bounding boxes if the intersection is not at a sentinel.
            // Go in the opposite direction from the neighbor intersection to find the border between the Steiner
            // point and vertexEvent.Site (unless vertexEvent.Site is inside the bounding box).
            if (ObstacleTree.GraphBox.Contains(lowSteiner))
            {
                var bboxIntersectBeforeLowSteiner = StaticGraphUtility.RectangleBorderIntersect(lowNborSide.Obstacle.VisibilityBoundingBox, lowSteiner, highDir);
                if (PointComparer.IsPureLower(bboxIntersectBeforeLowSteiner, vertexEvent.Site))
                {
                    this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeLowSteiner);
                }
            }
            if (ObstacleTree.GraphBox.Contains(highSteiner))
            {
                var bboxIntersectBeforeHighSteiner = StaticGraphUtility.RectangleBorderIntersect(highNborSide.Obstacle.VisibilityBoundingBox, highSteiner, lowDir);
                if (PointComparer.IsPureLower(vertexEvent.Site, bboxIntersectBeforeHighSteiner))
                {
                    this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeHighSteiner);
                }
            }

            // Add the corners of the bounding box of the vertex obstacle, if they are visible to the event site.
            // This ensures that we "go around" the obstacle, as with the non-orthogonal edges in the paper.
            Point lowCorner, highCorner;

            GetBoundingCorners(lowSideNode.Item.Obstacle.VisibilityBoundingBox, vertexEvent is OpenVertexEvent, this.ScanDirection.IsHorizontal,
                               out lowCorner, out highCorner);
            if (PointComparer.IsPureLower(lowSteiner, lowCorner) || lowNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle))
            {
                vertexPoints.Insert(lowCorner);
            }

            if (PointComparer.IsPureLower(highCorner, highSteiner) || highNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle))
            {
                vertexPoints.Insert(highCorner);
            }
        }
        bool IsSkippableSpliceSourceWithNullSpliceTarget(VisibilityVertex spliceSource, Directions extendDir)
        {
            if (IsSkippableSpliceSourceEdgeWithNullTarget(StaticGraphUtility.FindNextEdge(VisGraph, spliceSource, extendDir)))
            {
                return(true);
            }
            var spliceSourceEdge = StaticGraphUtility.FindNextEdge(this.VisGraph, spliceSource, CompassVector.OppositeDir(extendDir));

            // Since target is null, if this is a reflection, it is bouncing off an outer side of a group or
            // obstacle at spliceSource.  In that case, we don't want to splice from it because then we could
            // cut through the group and outside again; instead we should just stay outside it.
            return(IsSkippableSpliceSourceEdgeWithNullTarget(spliceSourceEdge) || IsReflectionEdge(spliceSourceEdge));
        }
Exemplo n.º 19
0
 internal void SetSides(Directions dir, RBNode <BasicObstacleSide> neighborNode, RBNode <BasicObstacleSide> overlapEndNode,
                        BasicObstacleSide interveningGroupSide)
 {
     if (StaticGraphUtility.IsAscending(dir))
     {
         HighNeighbor   = neighborNode;
         HighOverlapEnd = overlapEndNode;
         this.GroupSideInterveningBeforeHighNeighbor = interveningGroupSide;
         return;
     }
     LowNeighbor   = neighborNode;
     LowOverlapEnd = overlapEndNode;
     this.GroupSideInterveningBeforeLowNeighbor = interveningGroupSide;
 }
Exemplo n.º 20
0
        /// <summary>
        /// Create a LineSegment that contains the max visibility from startPoint in the desired direction.
        /// </summary>
        internal LineSegment CreateMaxVisibilitySegment(Point startPoint, Directions dir, out PointAndCrossingsList pacList)
        {
            var graphBoxBorderIntersect = StaticGraphUtility.RectangleBorderIntersect(this.GraphBox, startPoint, dir);

            if (PointComparer.GetDirections(startPoint, graphBoxBorderIntersect) == Directions.None)
            {
                pacList = null;
                return(new LineSegment(startPoint, startPoint));
            }
            var segment = this.RestrictSegmentWithObstacles(startPoint, graphBoxBorderIntersect);

            // Store this off before other operations which overwrite it.
            pacList = this.CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(segment.Start, segment.End);
            return(segment);
        }
Exemplo n.º 21
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, 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);
        }
Exemplo n.º 23
0
        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));
        }
Exemplo n.º 24
0
        // Adds an edge from this.Vertex to a (possibly new) vertex at an intersection with an
        // existing Edge that adjoins the point.  We take 'dir' as an input parameter for edge
        // extension because we may be on the edge so can't calculate the direction.
        internal VisibilityVertex AddEdgeToAdjacentEdge(TransientGraphUtility transUtil
                                                        , VisibilityEdge targetEdge, Directions dirToExtend, Rectangle limitRect)
        {
            Point            targetIntersect = StaticGraphUtility.SegmentIntersection(targetEdge, this.Point);
            VisibilityVertex targetVertex    = transUtil.VisGraph.FindVertex(targetIntersect);

            if (null != targetVertex)
            {
                AddToAdjacentVertex(transUtil, targetVertex, dirToExtend, limitRect);
            }
            else
            {
                targetVertex = transUtil.AddEdgeToTargetEdge(this.Vertex, targetEdge, targetIntersect);
            }
            ExtendEdgeChain(transUtil, targetVertex, dirToExtend, limitRect);
            return(targetVertex);
        }
Exemplo n.º 25
0
        internal VisibilityEdge FindNearestPerpendicularOrContainingEdge(VisibilityVertex startVertex
                                                                         , Directions 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.
            Directions dirTowardLocation = ~dir& PointComparer.GetDirections(startVertex.Point, pointLocation);

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

            // First move toward pointLocation far as we can.
            while (Directions.None != currentDirTowardLocation)
            {
                VisibilityVertex nextVertex = StaticGraphUtility.FindNextVertex(currentVertex, dirTowardLocation);
                if (null == nextVertex)
                {
                    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 ((null != perpEdge) || (currentVertex == startVertex))
                {
                    break;
                }

                currentVertex = StaticGraphUtility.FindNextVertex(currentVertex, CompassVector.OppositeDir(dirTowardLocation));
            }
            return(perpEdge);
        }
        private static VisibilityVertex TraverseToFirstVertexAtOrAbove(VisibilityVertex startVertex, Point start, Directions dir)
        {
            var returnVertex = startVertex;
            var oppositeDir  = CompassVector.OppositeDir(dir);

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

                // This returns Directions. None on a match.
                if ((null == nextVertex) || (PointComparer.GetDirections(nextVertex.Point, start) == oppositeDir))
                {
                    break;
                }
                returnVertex = nextVertex;
            }
            return(returnVertex);
        }
Exemplo n.º 27
0
        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);
        }
Exemplo n.º 28
0
        internal List <DebugCurve> Test_GetScanLineDebugCurves()
        {
// ReSharper restore InconsistentNaming
            var debugCurves = new List <DebugCurve>();

            // Alternate the colors between green and blue, so that any inconsistency will stand out.
            // Use red to highlight that.
            string[]          colors   = { "green", "blue" };
            int               index    = 0;
            var               bbox     = new Rectangle();
            BasicObstacleSide prevSide = null;

            foreach (var currentSide in SideTree)
            {
                string color = colors[index];
                index ^= 1;
                if (null == prevSide)
                {
                    // Create this the first time through; adding to an empty rectangle leaves 0,0.
                    bbox = new Rectangle(currentSide.Start, currentSide.End);
                }
                else
                {
                    if (-1 != Compare(prevSide, currentSide))
                    {
                        // Note: we toggled the index, so the red replaces the colour whose turn it is now
                        // and will leave the red line bracketed by two sides of the same colour.
                        color = "red";
                    }
                    bbox.Add(currentSide.Start);
                    bbox.Add(currentSide.End);
                }
                debugCurves.Add(new DebugCurve(0.1, color, new LineSegment(currentSide.Start, currentSide.End)));
                prevSide = currentSide;
            }

            // Add the sweep line.
            Point start = StaticGraphUtility.RectangleBorderIntersect(bbox, this.linePositionAtLastInsertOrRemove, scanDirection.OppositeDirection);
            Point end   = StaticGraphUtility.RectangleBorderIntersect(bbox, this.linePositionAtLastInsertOrRemove, scanDirection.Direction);

            debugCurves.Add(new DebugCurve(0.025, "black", new LineSegment(start, end)));
            return(debugCurves);
        }
        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);
            }
        }