Пример #1
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);
        }
Пример #2
0
 bool CompareToPoint(ScanSegment treeSeg)
 {
     // Test if treeSeg overlaps the LookupSegment.Start point.  We're using FindFirst,
     // so we'll just return false for everything that ends before the point and true for anything
     // that ends at or after it, then the caller will verify overlap.
     return(ScanDirection.Compare(treeSeg.End, this.lookupSegment.Start) >= 0);
 }
Пример #3
0
        /// <summary>
        /// For ordering the line segments inserted by the ScanLine. Assuming vertical sweep (sweeping up from
        /// bottom, scanning horizontally) then order ScanSegments first by lowest Y coord, then by lowest X coord.
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public int Compare(ScanSegment first, ScanSegment second)
        {
            if (first == second)
            {
                return(0);
            }
            if (first == null)
            {
                return(-1);
            }
            if (second == null)
            {
                return(1);
            }

            // This orders on both axes.
            int cmp = ScanDirection.Compare(first.Start, second.Start);

            if (0 == cmp)
            {
                // Longer segments come first, to make overlap removal easier.
                cmp = -ScanDirection.Compare(first.End, second.End);
            }
            return(cmp);
        }
Пример #4
0
        internal ScanSegment FindSegmentOverlappingPoints(Point start, Point end, bool allowUnfound)
        {
            this.lookupSegment.Update(start, end);
            RBNode <ScanSegment> node = this.segmentTree.FindFirst(this.findPointPred);

            // If we had any segments in the tree that end after 'start', node has the first one.
            // Now we need to that it starts before 'end'.  ScanSegment.CompareToPointPositionFullLength
            // asserts the point is on the segment which we don't want to require here, so
            // compare the endpoints directly.
            if (null != node)
            {
                ScanSegment seg = node.Item;
                if (ScanDirection.Compare(seg.Start, end) <= 0)
                {
                    return(seg);
                }
            }

            // Not found.
            if (!allowUnfound)
            {
                Debug.Assert(false, "Could not find expected segment");
            }

            return(null);
        }
Пример #5
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);
        }
Пример #6
0
        RBNode <ScanSegment> MergeAndRemoveNextNode(ScanSegment currentSegment, RBNode <ScanSegment> nextSegNode)
        {
            // Merge at the ends only - if we're here, start will be the same or greater.
            if (-1 == ScanDirection.Compare(currentSegment.End, nextSegNode.Item.End))
            {
                currentSegment.Update(currentSegment.Start, nextSegNode.Item.End);
            }

            // Removing the node can revise the tree's RBNodes internally so re-get the current segment.
            currentSegment.MergeGroupBoundaryCrossingList(nextSegNode.Item.GroupBoundaryPointAndCrossingsList);
            this.segmentTree.DeleteNodeInternal(nextSegNode);
            return(this.segmentTree.Find(currentSegment));
        }
Пример #7
0
        internal void MergeSegments()
        {
            // As described in the doc, hintScanSegment handles all the non-overlapped non-reflection cases.
            DevTraceInfo(1, "{0} ScanSegmentTree MergeSegments, count = {1}"
                         , ScanDirection.IsHorizontal ? "Horizontal" : "Vertical", this.segmentTree.Count);
            if (this.segmentTree.Count < 2)
            {
                return;
            }

            RBNode <ScanSegment> currentSegNode = this.segmentTree.TreeMinimum();
            RBNode <ScanSegment> nextSegNode    = this.segmentTree.Next(currentSegNode);

            for ( ; null != nextSegNode; nextSegNode = this.segmentTree.Next(currentSegNode))
            {
                DevTraceInfo(2, "Current {0}  Next {1}", currentSegNode.Item, nextSegNode.Item);
                int cmp = ScanDirection.Compare(nextSegNode.Item.Start, currentSegNode.Item.End);
                switch (cmp)
                {
                case 1:
                    // Next segment starts after the current one.
                    currentSegNode = nextSegNode;
                    break;

                case 0:
                    if (nextSegNode.Item.IsOverlapped == currentSegNode.Item.IsOverlapped)
                    {
                        // Overlapping is the same, so merge.  Because the ordering in the tree is that
                        // same-Start nodes are ordered by longest-End first, this will retain the tree ordering.
                        DevTraceInfo(2, "  (merged; start-at-end with same overlap)");
                        currentSegNode = MergeAndRemoveNextNode(currentSegNode.Item, nextSegNode);
                    }
                    else
                    {
                        // Touching start/end with differing IsOverlapped so they need a connecting vertex.
                        DevTraceInfo(2, "  (marked with NeedFinalOverlapVertex)");
                        currentSegNode.Item.NeedEndOverlapVertex = true;
                        nextSegNode.Item.NeedStartOverlapVertex  = true;
                        currentSegNode = nextSegNode;
                    }
                    break;

                default:        // -1 == cmp
                    // nextSegNode.Item.Start is before currentSegNode.Item.End.
                    Debug.Assert((nextSegNode.Item.Start != currentSegNode.Item.Start) ||
                                 (nextSegNode.Item.End < currentSegNode.Item.End)
                                 , "Identical segments are not allowed, and longer ones must come first");

                    // Because longer segments are ordered before shorter ones at the same start position,
                    // nextSegNode.Item must be a duplicate segment or is partially or totally overlapped.
                    // In the case of reflection lookahead segments, the side-intersection calculated from
                    // horizontal vs. vertical directions may be slightly different along the parallel
                    // coordinate from an overlapped segment, so let non-overlapped win that disagreement.
                    if (currentSegNode.Item.IsOverlapped != nextSegNode.Item.IsOverlapped)
                    {
                        Debug.Assert(ApproximateComparer.CloseIntersections(currentSegNode.Item.End, nextSegNode.Item.Start)
                                     , "Segments share a span with different IsOverlapped");
                        if (currentSegNode.Item.IsOverlapped)
                        {
                            // If the Starts are different, then currentSegNode is the only item at its
                            // start, so we don't need to re-insert.  Otherwise, we need to remove it and
                            // re-find nextSegNode's side.
                            if (currentSegNode.Item.Start == nextSegNode.Item.Start)
                            {
                                // currentSegNode is a tiny overlapped segment between two non-overlapped segments (so
                                // we'll have another merge later, when we hit the other non-overlapped segment).
                                // Notice reversed params.  TestNote: No longer have repro with the change to convex hulls;
                                // this may no longer happen since overlapped edges will now always be inside rectangular
                                // obstacles so there are no angled-side calculations.
                                currentSegNode = MergeAndRemoveNextNode(nextSegNode.Item, currentSegNode);
                                DevTraceInfo(2, "  (identical starts so discarding isOverlapped currentSegNode)");
                            }
                            else
                            {
                                currentSegNode.Item.Update(currentSegNode.Item.Start, nextSegNode.Item.Start);
                                DevTraceInfo(2, "  (trimming isOverlapped currentSegNode to {0})", currentSegNode.Item);
                                currentSegNode = nextSegNode;
                            }
                        }
                        else
                        {
                            if (currentSegNode.Item.End == nextSegNode.Item.End)
                            {
                                // nextSegNode is a tiny non-overlapped segment between two overlapped segments (so
                                // we'll have another merge later, when we hit the other non-overlapped segment).
                                // TestNote: No longer have repro with the change to convex hulls;
                                // this may no longer happen since overlapped edges will now always be inside rectangular
                                // obstacles so there are no angled-side calculations.
                                currentSegNode = MergeAndRemoveNextNode(currentSegNode.Item, nextSegNode);
                                DevTraceInfo(2, "  (identical ends so discarding isOverlapped currentSegNode)");
                            }
                            else
                            {
                                // Remove nextSegNode, increment its start to be after currentSegment, re-insert nextSegNode, and
                                // re-find currentSegNode (there may be more segments between nextSegment.Start and currentSegment.End).
                                ScanSegment nextSegment    = nextSegNode.Item;
                                ScanSegment currentSegment = currentSegNode.Item;
                                this.segmentTree.DeleteNodeInternal(nextSegNode);
                                nextSegment.Update(currentSegment.End, nextSegment.End);
                                this.segmentTree.Insert(nextSegment);
                                nextSegment.TrimGroupBoundaryCrossingList();
                                DevTraceInfo(2, "  (trimming isOverlapped nextSegNode to {0})", nextSegment);
                                currentSegNode = this.segmentTree.Find(currentSegment);
                            }
                        }
                        break;
                    }

                    // Overlaps match so do a normal merge operation.
                    DevTraceInfo(2, "  (merged; start-before-end)");
                    currentSegNode = MergeAndRemoveNextNode(currentSegNode.Item, nextSegNode);
                    break;
                } // endswitch
            }     // endfor

#if DEVTRACE
            DevTraceInfo(1, "{0} ScanSegmentTree after MergeSegments, count = {1}"
                         , ScanDirection.IsHorizontal ? "Horizontal" : "Vertical", this.segmentTree.Count);
            if (this.scanSegmentVerify.IsLevel(1))
            {
                DevTraceInfo(1, "{0} ScanSegmentTree Consistency Check"
                             , ScanDirection.IsHorizontal ? "Horizontal" : "Vertical");
                bool retval = true;
                RBNode <ScanSegment> prevSegNode = this.segmentTree.TreeMinimum();
                currentSegNode = this.segmentTree.Next(prevSegNode);
                while (null != currentSegNode)
                {
                    DevTraceInfo(4, currentSegNode.Item.ToString());
                    // We should only have end-to-end touching, and that only if differing IsOverlapped.
                    if ((-1 != Compare(prevSegNode.Item, currentSegNode.Item)) ||
                        (1 != ScanDirection.Compare(currentSegNode.Item.Start, prevSegNode.Item.Start) ||
                         ((0 == ScanDirection.Compare(currentSegNode.Item.Start, prevSegNode.Item.End)) &&
                          (currentSegNode.Item.IsOverlapped == prevSegNode.Item.IsOverlapped))))
                    {
                        this.scanSegmentTrace.WriteError(0, "Segments are not strictly increasing:");
                        this.scanSegmentTrace.WriteFollowup(0, prevSegNode.Item.ToString());
                        this.scanSegmentTrace.WriteFollowup(0, currentSegNode.Item.ToString());
                        retval = false;
                    }
                    prevSegNode    = currentSegNode;
                    currentSegNode = this.segmentTree.Next(currentSegNode);
                }
                Debug.Assert(retval, "ScanSegments are not strictly increasing");
            }
#endif // DEVTRACE
        }
Пример #8
0
 bool CompareIntersector(ScanSegment seg)
 {
     // We're looking for the last segment that starts before LookupSegment.Start.
     return(ScanDirection.Compare(seg.Start, this.lookupSegment.Start) <= 0);
 }