internal PointAndCrossingsList GetOrderedListBetween(Point start, Point end) {
            if (0 == pointCrossingMap.Count) {
                return null;
            }

            if (PointComparer.Compare(start, end) > 0) {
                Point temp = start;
                start = end;
                end = temp;
            }

            // Start and end are inclusive.
            pointList.Clear();
            foreach (var intersection in pointCrossingMap.Keys) {
                if ((PointComparer.Compare(intersection, start) >= 0) && (PointComparer.Compare(intersection, end) <= 0)) {
                    pointList.Add(intersection);
                }
            }

            pointList.Sort();
            var pointAndCrossingList = new PointAndCrossingsList();
            var numCrossings = pointList.Count;
            for (int ii = 0; ii < numCrossings; ++ii) {
                Point intersect = pointList[ii];
                pointAndCrossingList.Add(intersect, pointCrossingMap[intersect]);
            }
            return pointAndCrossingList;
        }
예제 #2
0
        internal void MergeFrom(PointAndCrossingsList other)
        {
            Reset();
            if ((null == other) || (0 == other.ListOfPointsAndCrossings.Count))
            {
                return;
            }

            if (0 == this.ListOfPointsAndCrossings.Count)
            {
                this.ListOfPointsAndCrossings.AddRange(other.ListOfPointsAndCrossings);
            }

            if (null == this.ListOfPointsAndCrossings)
            {
                this.ListOfPointsAndCrossings = new List <PointAndCrossings>(other.ListOfPointsAndCrossings);
                return;
            }

            // Do the usual sorted-list merge.
            int thisIndex = 0, thisMax = this.ListOfPointsAndCrossings.Count;
            int otherIndex = 0, otherMax = other.ListOfPointsAndCrossings.Count;
            var newCrossingsList = new List <PointAndCrossings>(this.ListOfPointsAndCrossings.Count);

            while ((thisIndex < thisMax) || (otherIndex < otherMax))
            {
                if (thisIndex >= thisMax)
                {
                    newCrossingsList.Add(other.ListOfPointsAndCrossings[otherIndex++]);
                    continue;
                }
                if (otherIndex >= otherMax)
                {
                    newCrossingsList.Add(this.ListOfPointsAndCrossings[thisIndex++]);
                    continue;
                }

                PointAndCrossings thisPac  = this.ListOfPointsAndCrossings[thisIndex];
                PointAndCrossings otherPac = other.ListOfPointsAndCrossings[otherIndex];
                int cmp = PointComparer.Compare(thisPac.Location, otherPac.Location);
                if (0 == cmp)
                {
                    // No duplicates
                    newCrossingsList.Add(thisPac);
                    ++thisIndex;
                    ++otherIndex;
                }
                else if (-1 == cmp)
                {
                    newCrossingsList.Add(thisPac);
                    ++thisIndex;
                }
                else
                {
                    newCrossingsList.Add(otherPac);
                    ++otherIndex;
                }
            }
            this.ListOfPointsAndCrossings = newCrossingsList;
        }
예제 #3
0
        internal PointAndCrossingsList GetOrderedListBetween(Point start, Point end)
        {
            if (0 == pointCrossingMap.Count)
            {
                return(null);
            }

            if (PointComparer.Compare(start, end) > 0)
            {
                Point temp = start;
                start = end;
                end   = temp;
            }

            // Start and end are inclusive.
            pointList.Clear();
            foreach (var intersection in pointCrossingMap.Keys)
            {
                if ((PointComparer.Compare(intersection, start) >= 0) && (PointComparer.Compare(intersection, end) <= 0))
                {
                    pointList.Add(intersection);
                }
            }

            pointList.Sort();
            var pointAndCrossingList = new PointAndCrossingsList();
            var numCrossings         = pointList.Count;

            for (int ii = 0; ii < numCrossings; ++ii)
            {
                Point intersect = pointList[ii];
                pointAndCrossingList.Add(intersect, pointCrossingMap[intersect]);
            }
            return(pointAndCrossingList);
        }
        // For group boundary crossings.

        internal void MergeGroupBoundaryCrossingList(PointAndCrossingsList other) {
            if (null != other) {
                if (null == GroupBoundaryPointAndCrossingsList) {
                    GroupBoundaryPointAndCrossingsList = new PointAndCrossingsList();
                }
                GroupBoundaryPointAndCrossingsList.MergeFrom(other);
            }
        }
예제 #5
0
 internal void OnSegmentIntersectorEnd(VisibilityGraph vg)
 {
     AppendGroupCrossingsThroughPoint(vg, End);
     GroupBoundaryPointAndCrossingsList = null;
     if ((null == HighestVisibilityVertex) || (PointComparer.IsPureLower(HighestVisibilityVertex.Point, End)))
     {
         LoadEndOverlapVertexIfNeeded(vg);
     }
 }
예제 #6
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;
                }
            }
        }
예제 #7
0
        // For group boundary crossings.

        internal void MergeGroupBoundaryCrossingList(PointAndCrossingsList other)
        {
            if (null != other)
            {
                if (null == GroupBoundaryPointAndCrossingsList)
                {
                    GroupBoundaryPointAndCrossingsList = new PointAndCrossingsList();
                }
                GroupBoundaryPointAndCrossingsList.MergeFrom(other);
            }
        }
예제 #8
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);
        }
        private void ExtendEdgeChain(VisibilityVertex startVertex, Directions extendDir
                                     , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment
                                     , PointAndCrossingsList pacList, bool isOverlapped)
        {
            StaticGraphUtility.Assert(PointComparer.GetPureDirection(maxDesiredSegment.Start, maxDesiredSegment.End) == extendDir
                                      , "maxDesiredSegment is reversed", ObstacleTree, VisGraph);

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

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

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

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

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

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

            SpliceGroupBoundaryCrossings(pacList, startVertex, maxDesiredSegment);
        }
        internal void MergeFrom(PointAndCrossingsList other) {
            Reset();
            if ((null == other) || (0 == other.ListOfPointsAndCrossings.Count)) {
                return;
            }
            if (0 == this.ListOfPointsAndCrossings.Count) {
                this.ListOfPointsAndCrossings.AddRange(other.ListOfPointsAndCrossings);
            }
            if (null == this.ListOfPointsAndCrossings) {
                this.ListOfPointsAndCrossings = new List<PointAndCrossings>(other.ListOfPointsAndCrossings);
                return;
            }

            // Do the usual sorted-list merge.
            int thisIndex = 0, thisMax = this.ListOfPointsAndCrossings.Count;
            int otherIndex = 0, otherMax = other.ListOfPointsAndCrossings.Count;
            var newCrossingsList = new List<PointAndCrossings>(this.ListOfPointsAndCrossings.Count);
            while ((thisIndex < thisMax) || (otherIndex < otherMax)) {
                if (thisIndex >= thisMax) {
                    newCrossingsList.Add(other.ListOfPointsAndCrossings[otherIndex++]);
                    continue;
                }
                if (otherIndex >= otherMax) {
                    newCrossingsList.Add(this.ListOfPointsAndCrossings[thisIndex++]);
                    continue;
                }

                PointAndCrossings thisPac = this.ListOfPointsAndCrossings[thisIndex];
                PointAndCrossings otherPac = other.ListOfPointsAndCrossings[otherIndex];
                int cmp = PointComparer.Compare(thisPac.Location, otherPac.Location);
                if (0 == cmp) {
                    // No duplicates
                    newCrossingsList.Add(thisPac);
                    ++thisIndex;
                    ++otherIndex;
                }
                else if (-1 == cmp) {
                    newCrossingsList.Add(thisPac);
                    ++thisIndex;
                }
                else {
                    newCrossingsList.Add(otherPac);
                    ++otherIndex;
                }
            }
            this.ListOfPointsAndCrossings = newCrossingsList;
        }
예제 #11
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);
        }
        private void SpliceGroupBoundaryCrossing(VisibilityVertex currentVertex, PointAndCrossings pac, Directions dirToInside)
        {
            GroupBoundaryCrossing[] crossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, dirToInside);
            if (null != crossings)
            {
                var outerVertex = VisGraph.FindVertex(pac.Location) ?? AddVertex(pac.Location);
                if (currentVertex.Point != outerVertex.Point)
                {
                    FindOrAddEdge(currentVertex, outerVertex);
                }
                var interiorPoint  = crossings[0].GetInteriorVertexPoint(pac.Location);
                var interiorVertex = VisGraph.FindVertex(interiorPoint) ?? AddVertex(interiorPoint);

                // FindOrAddEdge splits an existing edge so may not return the portion bracketed by outerVertex and interiorVertex.
                FindOrAddEdge(outerVertex, interiorVertex);
                var edge           = VisGraph.FindEdge(outerVertex.Point, interiorVertex.Point);
                var crossingsArray = crossings.Select(c => c.Group.InputShape).ToArray();
                edge.IsPassable = delegate { return(crossingsArray.Any(s => s.IsTransparent)); };
            }
        }
예제 #13
0
        internal void CreateSparseVerticesAndEdges(VisibilityGraph vg)
        {
            if (this.sparsePerpendicularCoords == null)
            {
                return;
            }

            AppendGroupCrossingsThroughPoint(vg, Start);
            foreach (var perpCoord in this.sparsePerpendicularCoords.OrderBy(d => d))
            {
                var vertexLocation = this.CreatePointFromPerpCoord(perpCoord);
                Debug.Assert(this.ContainsPoint(vertexLocation), "vertexLocation is not on Segment");
                this.AppendVisibilityVertex(vg, vg.FindVertex(vertexLocation) ?? vg.AddVertex(vertexLocation));
            }
            AppendGroupCrossingsThroughPoint(vg, End);
            GroupBoundaryPointAndCrossingsList = null;

            this.sparsePerpendicularCoords.Clear();
            this.sparsePerpendicularCoords = null;
        }
예제 #14
0
 /// <summary>
 /// Appends a ScanSegment to the linked list in the "Current" slot.
 /// </summary>
 internal void CreateScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList)
 {
     this.CurrentSlot.AppendScanSegment(new ScanSegment(start, end, weight, gbcList));
 }
        /// <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;
        }
 internal ScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList) {
     Update(start, end);
     Weight = weight;
     GroupBoundaryPointAndCrossingsList = gbcList;
 }
        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);
        }
        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;
                }
            }
        }
        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);
        }
예제 #20
0
        // If we have collinear segments, then we may be able to just update the previous one
        // instead of growing the ScanSegmentTree.
        //  - For multiple collinear OpenVertexEvents, neighbors to the high side have not yet
        //    been seen, so a segment is created that spans the lowest and highest neighbors.
        //    A subsequent collinear OpenVertexEvent will be to the high side and will add a
        //    subsegment of that segment, so we subsume it into LastAddedSegment.
        //  - For multiple collinear CloseVertexEvents, closing neighbors to the high side are
        //    still open, so a segment is created from the lowest neighbor to the next-highest
        //    collinear obstacle to be closed.  When that next-highest CloseVertexEvent is
        //    encountered, it will extend LastAddedSegment.
        //  - For multiple collinear mixed Open and Close events, we'll do all Opens first,
        //    followed by all closes (per EventQueue opening), so we may add multiple discrete
        //    segments, which ScanSegmentTree will merge.
        internal static bool Subsume(ref ScanSegment seg, Point newStart, Point newEnd,
                                     double weight, PointAndCrossingsList gbcList,
                                     ScanDirection scanDir, ScanSegmentTree tree, out bool extendStart,
                                     out bool extendEnd)
        {
            // Initialize these to the non-subsumed state; the endpoints were extended (or on a
            // different line).
            extendStart = true;
            extendEnd   = true;
            if (null == seg)
            {
                return(false);
            }

            // If they don't overlap (including touching at an endpoint), we don't subsume.
            if (!StaticGraphUtility.IntervalsOverlap(seg.Start, seg.End, newStart, newEnd))
            {
                return(false);
            }

            // If the overlapped-ness isn't the same, we don't subsume.  ScanSegmentTree::MergeSegments
            // will mark that the low-to-high direction needs a VisibilityVertex to link the two segments.
            // These may differ by more than Curve.DistanceEpsilon in the case of reflection lookahead
            // segments collinear with vertex-derived segments, so have a looser tolerance here and we'll
            // adjust the segments in ScanSegmentTree.MergeSegments.
            if (seg.Weight != weight)
            {
                if ((seg.Start == newStart) && (seg.End == newEnd))
                {
                    // This is probably because of a rounding difference by one DistanceEpsilon reporting being
                    // inside an obstacle vs. the scanline intersection calculation side-ordering.
                    // Test is RectilinearFileTests.Overlap_Rounding_Vertex_Intersects_Side.
                    seg.Weight = Math.Min(seg.Weight, weight);
                    return(true);
                }

                // In the case of groups, we go through the group boundary; this may coincide with a
                // reflection segment. RectilinearFileTests.ReflectionSubsumedBySegmentExitingGroup.
                Debug.Assert((seg.Weight == OverlappedWeight) == (weight == OverlappedWeight) ||
                             ApproximateComparer.CloseIntersections(seg.End, newStart) ||
                             ApproximateComparer.CloseIntersections(seg.Start, newEnd)
                             , "non-equal overlap-mismatched ScanSegments overlap by more than just Start/End");
                return(false);
            }

            // Subsume the input segment.  Return whether the start/end points were extended (newStart
            // is before this.Start, or newEnd is after this.End), so the caller can generate reflections
            // and so we can merge group border crossings.
            extendStart = (-1 == scanDir.CompareScanCoord(newStart, seg.Start));
            extendEnd   = (1 == scanDir.CompareScanCoord(newEnd, seg.End));
            if (extendStart || extendEnd)
            {
                // We order by start and end so need to replace this in the tree regardless of which end changes.
                tree.Remove(seg);
                seg.startPoint = scanDir.Min(seg.Start, newStart);
                seg.endPoint   = scanDir.Max(seg.End, newEnd);
                seg            = tree.InsertUnique(seg).Item;
                seg.MergeGroupBoundaryCrossingList(gbcList);
            }
            return(true);
        }
예제 #21
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);
        }
예제 #22
0
 internal ScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList)
 {
     Update(start, end);
     Weight = weight;
     GroupBoundaryPointAndCrossingsList = gbcList;
 }
        /// <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);
        }
        // If we have collinear segments, then we may be able to just update the previous one
        // instead of growing the ScanSegmentTree.
        //  - For multiple collinear OpenVertexEvents, neighbors to the high side have not yet
        //    been seen, so a segment is created that spans the lowest and highest neighbors.
        //    A subsequent collinear OpenVertexEvent will be to the high side and will add a
        //    subsegment of that segment, so we subsume it into LastAddedSegment.
        //  - For multiple collinear CloseVertexEvents, closing neighbors to the high side are
        //    still open, so a segment is created from the lowest neighbor to the next-highest
        //    collinear obstacle to be closed.  When that next-highest CloseVertexEvent is
        //    encountered, it will extend LastAddedSegment.
        //  - For multiple collinear mixed Open and Close events, we'll do all Opens first,
        //    followed by all closes (per EventQueue opening), so we may add multiple discrete
        //    segments, which ScanSegmentTree will merge.
        internal static bool Subsume(ref ScanSegment seg, Point newStart, Point newEnd,
                double weight, PointAndCrossingsList gbcList,
                ScanDirection scanDir, ScanSegmentTree tree, out bool extendStart,
                out bool extendEnd) {
            // Initialize these to the non-subsumed state; the endpoints were extended (or on a
            // different line).
            extendStart = true;
            extendEnd = true;
            if (null == seg) {
                return false;
            }

            // If they don't overlap (including touching at an endpoint), we don't subsume.
            if (!StaticGraphUtility.IntervalsOverlap(seg.Start, seg.End, newStart, newEnd)) {
                return false;
            }

            // If the overlapped-ness isn't the same, we don't subsume.  ScanSegmentTree::MergeSegments
            // will mark that the low-to-high direction needs a VisibilityVertex to link the two segments.
            // These may differ by more than Curve.DistanceEpsilon in the case of reflection lookahead
            // segments collinear with vertex-derived segments, so have a looser tolerance here and we'll
            // adjust the segments in ScanSegmentTree.MergeSegments.
            if (seg.Weight != weight) {
                if ((seg.Start == newStart) && (seg.End == newEnd)) {
                    // This is probably because of a rounding difference by one DistanceEpsilon reporting being
                    // inside an obstacle vs. the scanline intersection calculation side-ordering.
                    // Test is RectilinearFileTests.Overlap_Rounding_Vertex_Intersects_Side.
                    seg.Weight = Math.Min(seg.Weight, weight);
                    return true;
                }
                
                // In the case of groups, we go through the group boundary; this may coincide with a
                // reflection segment. RectilinearFileTests.ReflectionSubsumedBySegmentExitingGroup.
                Debug.Assert((seg.Weight == OverlappedWeight) == (weight == OverlappedWeight) ||
                                ApproximateComparer.CloseIntersections(seg.End, newStart) ||
                                ApproximateComparer.CloseIntersections(seg.Start, newEnd)
                        , "non-equal overlap-mismatched ScanSegments overlap by more than just Start/End");
                return false;
            }

            // Subsume the input segment.  Return whether the start/end points were extended (newStart
            // is before this.Start, or newEnd is after this.End), so the caller can generate reflections
            // and so we can merge group border crossings.
            extendStart = (-1 == scanDir.CompareScanCoord(newStart, seg.Start));
            extendEnd = (1 == scanDir.CompareScanCoord(newEnd, seg.End));
            if (extendStart || extendEnd) {
                // We order by start and end so need to replace this in the tree regardless of which end changes.
                tree.Remove(seg);
                seg.startPoint = scanDir.Min(seg.Start, newStart);
                seg.endPoint = scanDir.Max(seg.End, newEnd);
                seg = tree.InsertUnique(seg).Item;
                seg.MergeGroupBoundaryCrossingList(gbcList);
            }
            return true;
        }
        internal void CreateSparseVerticesAndEdges(VisibilityGraph vg) {
            if (this.sparsePerpendicularCoords == null) {
                return;
            }

            AppendGroupCrossingsThroughPoint(vg, Start);
            foreach (var perpCoord in this.sparsePerpendicularCoords.OrderBy(d => d)) {
                var vertexLocation = this.CreatePointFromPerpCoord(perpCoord);
                Debug.Assert(this.ContainsPoint(vertexLocation), "vertexLocation is not on Segment");
                this.AppendVisibilityVertex(vg, vg.FindVertex(vertexLocation) ?? vg.AddVertex(vertexLocation));
            }
            AppendGroupCrossingsThroughPoint(vg, End);
            GroupBoundaryPointAndCrossingsList = null;

            this.sparsePerpendicularCoords.Clear();
            this.sparsePerpendicularCoords = null;
        }
 /// <summary>
 /// Appends a ScanSegment to the linked list in the "Current" slot.
 /// </summary>
 internal void CreateScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList) {
     this.CurrentSlot.AppendScanSegment(new ScanSegment(start, end, weight, gbcList));
 }
 internal void OnSegmentIntersectorEnd(VisibilityGraph vg) {
     AppendGroupCrossingsThroughPoint(vg, End);
     GroupBoundaryPointAndCrossingsList = null;
     if ((null == HighestVisibilityVertex) || (PointComparer.IsPureLower(HighestVisibilityVertex.Point, End))) {
         LoadEndOverlapVertexIfNeeded(vg);
     }
 }