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.º 2
0
 internal bool ContainsPoint(Point test)
 {
     // This may be off the line so do not use GetPureDirections.
     return(PointComparer.Equal(this.Start, test) ||
            PointComparer.Equal(this.End, test) ||
            (PointComparer.GetDirections(this.Start, test) == PointComparer.GetDirections(test, this.End)));
 }
Exemplo n.º 3
0
        internal RBNode <ScanSegment> FindLowestIntersectorNode(Point start, Point end)
        {
            Debug.Assert(ScanDirection.IsPerpendicular(start, end), "non-perpendicular segment passed");

            // Find the last segment that starts at or before 'start'.
            this.lookupSegment.Update(start, start);
            RBNode <ScanSegment> node = this.segmentTree.FindLast(this.findIntersectorPred);

            // We have a segment that intersects start/end, or one that ends before 'start' and thus we
            // must iterate to find the lowest bisector.  TODOperf: see how much that iteration costs us
            // (here and Highest); consider a BSP tree or interval tree (maybe 2-d RBTree for updatability).
            if (PointComparer.Equal(start, end))
            {
                if ((null != node) && (ScanDirection.Compare(node.Item.End, start) < 0))
                {
                    node = null;
                }
            }
            else
            {
                this.lookupSegment.Update(start, end);
                while ((null != node) && !node.Item.IntersectsSegment(this.lookupSegment))
                {
                    // If the node segment starts after 'end', no intersection was found.
                    if (ScanDirection.Compare(node.Item.Start, end) > 0)
                    {
                        return(null);
                    }

                    node = this.segmentTree.Next(node);
                }
            }
            return(node);
        }
        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));
        }
Exemplo n.º 5
0
            /// <summary>
            /// Move along the linked list until we hit the ScanSegment that contains the point.
            /// </summary>
            internal bool TraverseToSegmentContainingPoint(Point point)
            {
                // This is not a simple Next() because scan segments are extended "through" obstacles
                // (intermixing overlapped and non-overlapped) and thus a ScanSegment's Start and End
                // may not be in the vertexPoints collection and the ScanSegment must be skipped.
                if (this.CurrentSegment.ContainsPoint(point))
                {
                    return(true);
                }

                var pointCoord = this.IsHorizontal ? point.Y : point.X;

                if (!PointComparer.Equal(this.Coord, pointCoord))
                {
                    Debug.Assert(PointComparer.Compare(this.Coord, pointCoord) == -1, "point is before current Coord");
                    while (this.MoveNext())
                    {
                        // Skip to the end of the linked list if this point is not on the same coordinate.
                    }
                    return(false);
                }

                for (;;)
                {
                    // In the event of mismatched rounding on horizontal versus vertical intersections
                    // with a sloped obstacle side, we may have a point that is just before or just
                    // after the current segment.  If the point is in some space that doesn't have a
                    // scansegment, and if we are "close enough" to one end or the other of a scansegment,
                    // then grow the scansegment enough to include the new point.
                    if ((null == this.CurrentSegment.NextSegment) ||
                        PointComparer.GetDirections(this.CurrentSegment.End, point)
                        == PointComparer.GetDirections(point, this.CurrentSegment.NextSegment.Start))
                    {
                        if (ApproximateComparer.CloseIntersections(this.CurrentSegment.End, point))
                        {
                            this.CurrentSegment.Update(this.CurrentSegment.Start, point);
                            return(true);
                        }
                    }

                    if (!this.MoveNext())
                    {
                        return(false);
                    }

                    if (this.CurrentSegment.ContainsPoint(point))
                    {
                        return(true);
                    }

                    // This is likely the reverse of the above; the point rounding mismatched to just before
                    // rather than just after the current segment.
                    if (PointComparer.IsPureLower(point, this.CurrentSegment.Start))
                    {
                        Debug.Assert(ApproximateComparer.CloseIntersections(this.CurrentSegment.Start, point), "Skipped over the point in the ScanSegment linked list");
                        this.CurrentSegment.Update(point, this.CurrentSegment.End);
                        return(true);
                    }
                }
            }
        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.º 7
0
        // Find the highest perpendicular scanseg that intersects the segment endpoints.
        internal ScanSegment FindHighestIntersector(Point start, Point end)
        {
            Debug.Assert(ScanDirection.IsPerpendicular(start, end), "non-perpendicular segment passed");

            // Find the last segment that starts at or before 'end'.
            this.lookupSegment.Update(end, end);
            RBNode <ScanSegment> node = this.segmentTree.FindLast(this.findIntersectorPred);

            // Now we either have a segment that intersects start/end, or one that ends before
            // 'end' and need to iterate to find the highest bisector.
            if (PointComparer.Equal(start, end))
            {
                if ((null != node) && (ScanDirection.Compare(node.Item.End, start) < 0))
                {
                    node = null;
                }
            }
            else
            {
                this.lookupSegment.Update(start, end);
                while ((null != node) && !node.Item.IntersectsSegment(this.lookupSegment))
                {
                    // If the node segment ends before 'start', no intersection was found.
                    if (ScanDirection.Compare(node.Item.End, start) < 0)
                    {
                        return(null);
                    }

                    node = this.segmentTree.Previous(node);
                }
            }
            return((null != node) ? node.Item : null);
        }
        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);
        }
        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);
        }
Exemplo n.º 10
0
 private void AppendHighestVisibilityVertex(VisibilityVertex newVertex)
 {
     if (!PointComparer.Equal(HighestVisibilityVertex.Point, newVertex.Point))
     {
         AddVisibilityEdge(HighestVisibilityVertex, newVertex);
         HighestVisibilityVertex = newVertex;
     }
 }
Exemplo n.º 11
0
        internal bool SegmentCrossesANonGroupObstacle(Point startPoint, Point endPoint)
        {
            stopAtGroups       = false;
            wantGroupCrossings = false;
            LineSegment obstacleIntersectSeg = RestrictSegmentPrivate(startPoint, endPoint);

            return(!PointComparer.Equal(obstacleIntersectSeg.End, endPoint));
        }
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
 internal void AddToAdjacentVertex(TransientGraphUtility transUtil
                                   , VisibilityVertex targetVertex, Directions dirToExtend, Rectangle limitRect)
 {
     if (!PointComparer.Equal(this.Point, targetVertex.Point))
     {
         transUtil.FindOrAddEdge(this.Vertex, targetVertex, InitialWeight);
     }
     ExtendEdgeChain(transUtil, targetVertex, dirToExtend, limitRect);
 }
Exemplo n.º 14
0
        static internal bool IntervalsAreCollinear(Point start1, Point end1, Point start2, Point end2)
        {
            Debug.Assert(IsVertical(start1, end1) == IsVertical(start2, end2), "segments are not in the same orientation");
            bool vertical = IsVertical(start1, end1);

            if (IsVertical(start2, end2) == vertical)
            {
                // This handles touching endpoints as well.
                return(vertical ? PointComparer.Equal(start1.X, start2.X) : PointComparer.Equal(start1.Y, start2.Y));
            }
            return(false);
        }
Exemplo n.º 15
0
        private bool AppendGroupCrossingsThroughPoint(VisibilityGraph vg, Point lastPoint)
        {
            if (null == GroupBoundaryPointAndCrossingsList)
            {
                return(false);
            }

            bool found = false;

            while (GroupBoundaryPointAndCrossingsList.CurrentIsBeforeOrAt(lastPoint))
            {
                // We will only create crossing Edges that the segment actually crosses, not those it ends before crossing.
                // For those terminal crossings, the adjacent segment creates the interior vertex and crossing edge.
                PointAndCrossings       pac              = GroupBoundaryPointAndCrossingsList.Pop();
                GroupBoundaryCrossing[] lowDirCrossings  = null;
                GroupBoundaryCrossing[] highDirCrossings = null;
                if (PointComparer.Compare(pac.Location, Start) > 0)
                {
                    lowDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings,
                                                                            ScanDirection.OppositeDirection);
                }
                if (PointComparer.Compare(pac.Location, End) < 0)
                {
                    highDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, ScanDirection.Direction);
                }

                found = true;
                VisibilityVertex crossingVertex = vg.FindVertex(pac.Location) ?? vg.AddVertex(pac.Location);

                if ((null != lowDirCrossings) || (null != highDirCrossings))
                {
                    AddLowCrossings(vg, crossingVertex, lowDirCrossings);
                    AddHighCrossings(vg, crossingVertex, highDirCrossings);
                }
                else
                {
                    // This is at this.Start with only lower-direction toward group interior(s), or at this.End with only
                    // higher-direction toward group interior(s).  Therefore an adjacent ScanSegment will create the crossing
                    // edge, so create the crossing vertex here and we'll link to it.
                    if (null == LowestVisibilityVertex)
                    {
                        SetInitialVisibilityVertex(crossingVertex);
                    }
                    else
                    {
                        Debug.Assert(PointComparer.Equal(End, crossingVertex.Point), "Expected this.End crossingVertex");
                        AppendHighestVisibilityVertex(crossingVertex);
                    }
                }
            }
            return(found);
        }
Exemplo n.º 16
0
        internal ScanSegment Find(Point start, Point end)
        {
            Debug.Assert(PointComparer.Equal(start, end) || !ScanDirection.IsPerpendicular(start, end)
                         , "perpendicular segment passed");
            this.lookupSegment.Update(start, end);
            RBNode <ScanSegment> node = this.segmentTree.Find(this.lookupSegment);

            if ((null != node) && PointComparer.Equal(node.Item.End, end))
            {
                return(node.Item);
            }
            return(null);
        }
Exemplo n.º 17
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.º 18
0
 /// <summary>
 /// Add an EdgeGeometry to route
 /// </summary>
 /// <param name="edgeGeometry"></param>
 public void AddEdgeGeometryToRoute(EdgeGeometry edgeGeometry)
 {
     ValidateArg.IsNotNull(edgeGeometry, "edgeGeometry");
     // The Port.Location values are not necessarily rounded by the caller.  The values
     // will be rounded upon acquisition in PortManager.cs.  PointComparer.Equal expects
     // all values to be rounded.
     if (!PointComparer.Equal(ApproximateComparer.Round(edgeGeometry.SourcePort.Location)
                              , ApproximateComparer.Round(edgeGeometry.TargetPort.Location)))
     {
         EdgeGeometries.Add(edgeGeometry);
     }
     else
     {
         selfEdges.Add(edgeGeometry);
     }
 }
Exemplo n.º 19
0
        private void AddCrossingEdge(VisibilityGraph vg, VisibilityVertex lowVertex, VisibilityVertex highVertex, GroupBoundaryCrossing[] crossings)
        {
            VisibilityEdge edge = null;

            if (null != HighestVisibilityVertex)
            {
                // We may have a case where point xx.xxxxx8 has added an ascending-direction crossing, and now we're on
                // xx.xxxxx9 adding a descending-direction crossing.  In that case there should already be a VisibilityEdge
                // in the direction we want.
                if (PointComparer.Equal(this.HighestVisibilityVertex.Point, highVertex.Point))
                {
                    edge = vg.FindEdge(lowVertex.Point, highVertex.Point);
                    Debug.Assert(edge != null, "Inconsistent forward-backward sequencing in HighVisibilityVertex");
                }
                else
                {
                    AppendHighestVisibilityVertex(lowVertex);
                }
            }
            if (edge == null)
            {
                edge = AddVisibilityEdge(lowVertex, highVertex);
            }

            var crossingsArray = crossings.Select(c => c.Group.InputShape).ToArray();
            var prevIsPassable = edge.IsPassable;

            if (prevIsPassable == null)
            {
                edge.IsPassable = delegate { return(crossingsArray.Any(s => s.IsTransparent)); }
            }
            ;
            else
            {
                // Because we don't have access to the previous delegate's internals, we have to chain.  Fortunately this
                // will never be more than two deep.  File Test: Groups_Forward_Backward_Between_Same_Vertices.
                edge.IsPassable = delegate { return(crossingsArray.Any(s => s.IsTransparent) || prevIsPassable()); };
            }
            if (null == LowestVisibilityVertex)
            {
                SetInitialVisibilityVertex(lowVertex);
            }

            HighestVisibilityVertex = highVertex;
        }
Exemplo n.º 20
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);
        }
        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);
        }
Exemplo n.º 22
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);
        }
Exemplo n.º 23
0
        // Return value is whether or not we added a new segment.
        bool AddSegment(Point start, Point end, Obstacle eventObstacle
                        , BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide
                        , SweepEvent action, double weight)
        {
            DevTraceInfoVgGen(1, "Adding Segment [{0} -> {1} {2}] weight {3}", start, end, weight);
            DevTraceInfoVgGen(2, "     side {0}", lowNborSide);
            DevTraceInfoVgGen(2, "  -> side {0}", highNborSide);
            if (PointComparer.Equal(start, end))
            {
                return(false);
            }

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

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

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

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

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

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

            DevTraceInfoVgGen(2, "HintScanSegment {0}{1}", hintScanSegment, wasSubsumed ? " (subsumed)" : "");
            DevTrace_DumpScanSegmentsDuringAdd(3);
            return(true);
        }
Exemplo n.º 24
0
 static internal bool PointIsOnSegment(Point first, Point second, Point test)
 {
     return(PointComparer.Equal(first, test) ||
            PointComparer.Equal(second, test) ||
            (PointComparer.GetPureDirection(first, test) == PointComparer.GetPureDirection(test, second)));
 }
Exemplo n.º 25
0
 static internal bool IntervalsAreSame(Point start1, Point end1, Point start2, Point end2)
 {
     return(PointComparer.Equal(start1, start2) && PointComparer.Equal(end1, end2));
 }
Exemplo n.º 26
0
/// <summary>
        /// Route a single stage of a possibly multi-stage (due to waypoints) path.
        /// </summary>
        /// <param name="sourceVertexEntries">The VertexEntry array that was in the source vertex if it was the target of a prior stage.</param>
        /// <param name="sources">The enumeration of source vertices; must be only one if sourceVertexEntries is non-null.</param>
        /// <param name="targets">The enumeration of target vertex entries; must be only one if targetVertexEntries is non-null.</param>
        /// <param name="targetVertexEntries">The VertexEntry array that is in the target at the end of the stage.</param>
        private VertexEntry GetPathStage(VertexEntry[] sourceVertexEntries, IEnumerable <VisibilityVertex> sources,
                                         VertexEntry[] targetVertexEntries, IEnumerable <VisibilityVertex> targets)
        {
            var         ssstCalculator = new SsstRectilinearPath();
            VertexEntry bestEntry      = null;

            // This contains the best (lowest) path cost after normalizing origins to the center of the sources
            // and targets.  This is used to avoid selecting a vertex pair whose path has more bends than another pair of
            // vertices, but the bend penalty didn't total enough to offset the additional length between the "better" pair.
            // This also plays the role of an upper bound on the path length; if a path cost is greater than adjustedMinCost
            // then we stop exploring it, which saves considerable time after low-cost paths have been found.
            double bestCost          = double.MaxValue / ScanSegment.OverlappedWeight;
            double bestPathCostRatio = double.PositiveInfinity;

            // Calculate the bend penalty multiplier.  This is a percentage of the distance between the source and target,
            // so that we have the same relative importance if we have objects of about size 20 that are about 100 apart
            // as for objects of about size 200 that are about 1000 apart.
            Point sourceCenter = Barycenter(sources);
            Point targetCenter = Barycenter(targets);
            var   distance     = SsstRectilinearPath.ManhattanDistance(sourceCenter, targetCenter);

            ssstCalculator.BendsImportance = Math.Max(0.001, distance * (this.bendPenaltyAsAPercentageOfDistance * 0.01));

            // We'll normalize by adding (a proportion of) the distance (only; not bends) from the current endpoints to
            // their centers. This is similar to routeToCenter, but routing multiple paths like this means we'll always
            // get at least a tie for the best vertex pair, whereas routeToCenter can introduce extraneous bends
            // if the sources/targets are not collinear with the center (such as an E-R diagram).
            // interiorLengthAdjustment is a way to decrease the cost adjustment slightly to allow a bend if it saves moving
            // a certain proportion of the distance parallel to the object before turning to it.
            var interiorLengthAdjustment = ssstCalculator.LengthImportance;

            // VertexEntries for the current pass of the current stage, if multistage.
            var tempTargetEntries = (targetVertexEntries != null) ? this.currentPassTargetEntries : null;

            // Process closest pairs first, so we can skip longer ones (jump out of SsstRectilinear sooner, often immediately).
            // This means that we'll be consistent on tiebreaking for equal scores with differing bend counts (the shorter
            // path will win).  In overlapped graphs the shortest path may have more higher-weight edges.
            foreach (var pair in
                     from VisibilityVertexRectilinear source in sources
                     from VisibilityVertexRectilinear target in targets
                     orderby SsstRectilinearPath.ManhattanDistance(source.Point, target.Point)
                     select new { sourceV = source, targetV = target })
            {
                var source = pair.sourceV;
                var target = pair.targetV;
                if (PointComparer.Equal(source.Point, target.Point))
                {
                    continue;
                }
                var sourceCostAdjustment = SsstRectilinearPath.ManhattanDistance(source.Point, sourceCenter) * interiorLengthAdjustment;
                var targetCostAdjustment = SsstRectilinearPath.ManhattanDistance(target.Point, targetCenter) * interiorLengthAdjustment;

                var adjustedBestCost = bestCost;
                if (targetVertexEntries != null)
                {
                    Array.Clear(tempTargetEntries, 0, tempTargetEntries.Length);
                    adjustedBestCost = ssstCalculator.MultistageAdjustedCostBound(bestCost);
                }
                VertexEntry lastEntry = ssstCalculator.GetPathWithCost(sourceVertexEntries, source, sourceCostAdjustment,
                                                                       tempTargetEntries, target, targetCostAdjustment,
                                                                       adjustedBestCost);
                if (tempTargetEntries != null)
                {
                    UpdateTargetEntriesForEachDirection(targetVertexEntries, tempTargetEntries, ref bestCost, ref bestEntry);
                    continue;
                }

                // This is the final (or only) stage. Break ties by picking the lowest ratio of cost to ManhattanDistance between the endpoints.
                if (lastEntry == null)
                {
                    continue;
                }
                var costRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                if ((lastEntry.Cost < bestCost) || ApproximateComparer.Close(lastEntry.Cost, bestCost) && (costRatio < bestPathCostRatio))
                {
                    bestCost          = lastEntry.Cost;
                    bestEntry         = lastEntry;
                    bestPathCostRatio = lastEntry.Cost / SsstRectilinearPath.ManhattanDistance(source.Point, target.Point);
                }
            }
            return(bestEntry);
        }
 static bool DevTrace_IsEndOfEdge(VisibilityEdge e, Point x)
 {
     return(PointComparer.Equal(e.SourcePoint, x) || PointComparer.Equal(e.TargetPoint, x));
 }
 static bool IsSkippableSpliceSourceEdgeWithNullTarget(VisibilityEdge spliceSourceEdge)
 {
     return((null != spliceSourceEdge) &&
            (null != spliceSourceEdge.IsPassable) &&
            (PointComparer.Equal(spliceSourceEdge.Length, GroupBoundaryCrossing.BoundaryWidth)));
 }
 internal bool IsPerpendicular(Point start, Point end)
 {
     // Return true if there is no change in the primary direction.
     return(PointComparer.Equal((end - start) * DirectionAsPoint, 0.0));
 }