Пример #1
0
 internal ScanSegmentTree(ScanDirection scanDir)
 {
     ScanDirection            = scanDir;
     this.segmentTree         = new RbTree <ScanSegment>(this);
     this.findIntersectorPred = new Func <ScanSegment, bool>(this.CompareIntersector);
     this.findPointPred       = new Func <ScanSegment, bool>(this.CompareToPoint);
 }
 protected VisibilityGraphGenerator(bool wantReflections) {
     this.ScanDirection = ScanDirection.HorizontalInstance;
     this.eventQueue = new EventQueue();
     this.HorizontalScanSegments = new ScanSegmentTree(ScanDirection.HorizontalInstance);
     this.VerticalScanSegments = new ScanSegmentTree(ScanDirection.VerticalInstance);
     this.wantReflections = wantReflections;
 }
Пример #3
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);
        }
Пример #4
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);
        }
Пример #5
0
        internal static Obstacle CreateSentinel(Point a, Point b, ScanDirection scanDir, int scanlineOrdinal)
        {
            var sentinel = new Obstacle(a, b, scanlineOrdinal);

            sentinel.CreateInitialSides(sentinel.PaddedPolyline.StartPoint, scanDir);
            return(sentinel);
        }
Пример #6
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);
 }
Пример #7
0
        static internal double Slope(Point start, Point end, ScanDirection scanDir)
        {
            // Find the slope relative to scanline - how much scan coord changes per sweep change.
            Point lineDirAsPoint = end - start;

            return((lineDirAsPoint * scanDir.PerpDirectionAsPoint) / (lineDirAsPoint * scanDir.DirectionAsPoint));
        }
Пример #8
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);
        }
Пример #9
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);
        }
        // Hit-testing.
        internal bool IntersectionIsInsideAnotherObstacle(Obstacle sideObstacle, Obstacle eventObstacle, Point intersect, ScanDirection scanDirection)
        {
            insideHitTestIgnoreObstacle1 = eventObstacle;
            insideHitTestIgnoreObstacle2 = sideObstacle;
            insideHitTestScanDirection   = scanDirection;
            RectangleNode <Obstacle, Point> obstacleNode = Root.FirstHitNode(intersect, InsideObstacleHitTest);

            return(null != obstacleNode);
        }
        internal bool PointIsInsideAnObstacle(Point intersect, ScanDirection scanDirection)
        {
            insideHitTestIgnoreObstacle1 = null;
            insideHitTestIgnoreObstacle2 = null;
            insideHitTestScanDirection   = scanDirection;
            RectangleNode <Obstacle, Point> obstacleNode = Root.FirstHitNode(intersect, InsideObstacleHitTest);

            return(null != obstacleNode);
        }
 internal BasicObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir, bool traverseClockwise)
     : base(startVertex)
 {
     Obstacle = obstacle;
     endVertex = traverseClockwise ? startVertex.NextOnPolyline : startVertex.PrevOnPolyline;
     if (!scanDir.IsPerpendicular(startVertex.Point, endVertex.Point))
     {
         Slope = StaticGraphUtility.Slope(startVertex.Point, endVertex.Point, scanDir);
         SlopeInverse = 1.0 / Slope;
     }
 }
Пример #13
0
 internal BasicObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir, bool traverseClockwise)
     : base(startVertex)
 {
     Obstacle  = obstacle;
     endVertex = traverseClockwise ? startVertex.NextOnPolyline : startVertex.PrevOnPolyline;
     if (!scanDir.IsPerpendicular(startVertex.Point, endVertex.Point))
     {
         Slope        = StaticGraphUtility.Slope(startVertex.Point, endVertex.Point, scanDir);
         SlopeInverse = 1.0 / Slope;
     }
 }
Пример #14
0
 // Set the initial ActiveLowSide and ActiveHighSide of the obstacle starting at this point.
 internal void CreateInitialSides(PolylinePoint startPoint, ScanDirection scanDir)
 {
     Debug.Assert((null == ActiveLowSide) && (null == ActiveHighSide)
                  , "Cannot call SetInitialSides when sides are already set");
     ActiveLowSide  = new LowObstacleSide(this, startPoint, scanDir);
     ActiveHighSide = new HighObstacleSide(this, startPoint, scanDir);
     if (scanDir.IsFlat(ActiveHighSide))
     {
         // No flat sides in the scanline; we'll do lookahead processing in the scanline to handle overlaps
         // with existing segments, and normal neighbor handling will take care of collinear OpenVertexEvents.
         ActiveHighSide = new HighObstacleSide(this, ActiveHighSide.EndVertex, scanDir);
     }
 }
Пример #15
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);
        }
Пример #16
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));
        }
Пример #17
0
 private void SetVectorsAndCoordMaps(ScanDirection scanDir)
 {
     if (scanDir.IsHorizontal)
     {
         this.parallelSegmentVector      = this.horizontalScanSegmentVector;
         this.perpendicularSegmentVector = this.verticalScanSegmentVector;
         this.perpendicularCoordMap      = this.verticalCoordMap;
     }
     else
     {
         this.parallelSegmentVector      = this.verticalScanSegmentVector;
         this.perpendicularSegmentVector = this.horizontalScanSegmentVector;
         this.perpendicularCoordMap      = this.horizontalCoordMap;
     }
 }
Пример #18
0
 private void AddAxisCoordinateEvents(ScanDirection scanDir)
 {
     // Normal event ordering will apply - and will thus order the ScanSegments created in the vectors.
     if (scanDir.IsHorizontal)
     {
         foreach (var coord in yCoordAccumulator)
         {
             base.eventQueue.Enqueue(new AxisCoordinateEvent(new Point(ObstacleTree.GraphBox.Left - SentinelOffset, coord)));
         }
         return;
     }
     foreach (var coord in xCoordAccumulator)
     {
         base.eventQueue.Enqueue(new AxisCoordinateEvent(new Point(coord, ObstacleTree.GraphBox.Bottom - SentinelOffset)));
     }
 }
 internal HighObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsVertical /*traverseClockwise*/)
 {
 }
 internal bool PointIsInsideAnObstacle(Point intersect, ScanDirection scanDirection) {
     insideHitTestIgnoreObstacle1 = null;
     insideHitTestIgnoreObstacle2 = null;
     insideHitTestScanDirection = scanDirection;
     RectangleNode<Obstacle> obstacleNode = Root.FirstHitNode(intersect, InsideObstacleHitTest);
     return (null != obstacleNode);
 }
 static internal double Slope(SegmentBase seg, ScanDirection scanDir)
 {
     return(Slope(seg.Start, seg.End, scanDir));
 }
Пример #22
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);
        }
 internal HighObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsVertical /*traverseClockwise*/)
 {
 }
 internal virtual void InitializeEventQueue(ScanDirection scanDir) {
     ScanDirection = scanDir;
     eventQueue.Reset(ScanDirection);
     EnqueueBottomVertexEvents();
     scanLine = new RectilinearScanLine(ScanDirection, ObstacleTree.GraphBox.LeftBottom);
     lookaheadScan = new LookaheadScan(ScanDirection);
 }
Пример #25
0
 internal override void InitializeEventQueue(ScanDirection scanDir)
 {
     base.InitializeEventQueue(scanDir);
     hintScanSegment = null;
 }
 internal override void InitializeEventQueue(ScanDirection scanDir) {
     base.InitializeEventQueue(scanDir);
     hintScanSegment = null;
 }
Пример #27
0
 internal void Reset(ScanDirection scanDir)
 {
     Debug.Assert(0 == eventTree.Count, "Stray events in EventQueue.Reset");
     scanDirection = scanDir;
 }
Пример #28
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);
 }
 static internal double Slope(SegmentBase seg, ScanDirection scanDir) {
     return Slope(seg.Start, seg.End, scanDir);
 }
Пример #30
0
 internal LookaheadScan(ScanDirection scanDir)
 {
     scanDirection = scanDir;
     eventTree     = new RbTree <BasicReflectionEvent>(this);
     findFirstPred = new Func <BasicReflectionEvent, bool>(n => CompareToFindFirstPoint(n.Site) >= 0);
 }
Пример #31
0
        internal ObstaclePortEntrance(ObstaclePort oport, Point unpaddedBorderIntersect, Direction outDir, ObstacleTree obstacleTree)
        {
            ObstaclePort            = oport;
            UnpaddedBorderIntersect = unpaddedBorderIntersect;
            OutwardDirection        = outDir;

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

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

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

            // Groups are never in a clump (overlapped) but they may still have their port entrance overlapped.
            if (this.Obstacle.IsOverlapped || (this.Obstacle.IsGroup && !this.Obstacle.IsInConvexHull))
            {
                this.IsOverlapped = obstacleTree.IntersectionIsInsideAnotherObstacle(/*sideObstacle:*/ null, this.Obstacle
                                                                                     , this.VisibilityBorderIntersect, ScanDirection.GetInstance(OutwardDirection));
                if (!this.Obstacle.IsGroup || this.IsOverlapped || this.InteriorEdgeCrossesObstacle(obstacleTree))
                {
                    unpaddedToPaddedBorderWeight = ScanSegment.OverlappedWeight;
                }
            }
            if (this.Obstacle.IsInConvexHull && (unpaddedToPaddedBorderWeight == ScanSegment.NormalWeight))
            {
                SetUnpaddedToPaddedBorderWeightFromHullSiblingOverlaps(obstacleTree);
            }
        }
 // Hit-testing.
 internal bool IntersectionIsInsideAnotherObstacle(Obstacle sideObstacle, Obstacle eventObstacle, Point intersect, ScanDirection scanDirection) {
     insideHitTestIgnoreObstacle1 = eventObstacle;
     insideHitTestIgnoreObstacle2 = sideObstacle;
     insideHitTestScanDirection = scanDirection;
     RectangleNode<Obstacle> obstacleNode = Root.FirstHitNode(intersect, InsideObstacleHitTest);
     return (null != obstacleNode);
 }
Пример #33
0
 internal RectilinearScanLine(ScanDirection scanDir, Point start)
 {
     scanDirection = scanDir;
     SideTree      = new RbTree <BasicObstacleSide>(this);
     this.linePositionAtLastInsertOrRemove = start;
 }
Пример #34
0
 void AssertValidSegmentForInsertion(ScanSegment seg)
 {
     Debug.Assert((seg.End.X >= seg.Start.X) && (seg.End.Y >= seg.Start.Y), "Reversed direction in ScanSegment");
     Debug.Assert(ScanDirection.IsFlat(seg.Start, seg.End), "non-flat segment cannot be inserted");
 }
 internal LowObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsHorizontal /*traverseClockwise*/)
 {
 }
Пример #36
0
 internal LowObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir)
     : base(obstacle, startVertex, scanDir, scanDir.IsHorizontal /*traverseClockwise*/)
 {
 }
 internal bool PointIsInsideAnObstacle(Point intersect, Directions direction)
 {
     return(PointIsInsideAnObstacle(intersect, ScanDirection.GetInstance(direction)));
 }
Пример #38
0
 internal override void InitializeEventQueue(ScanDirection scanDir)
 {
     base.InitializeEventQueue(scanDir);
     this.SetVectorsAndCoordMaps(scanDir);
     this.AddAxisCoordinateEvents(scanDir);
 }
Пример #39
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
        }
 static internal double Slope(Point start, Point end, ScanDirection scanDir) {
     // Find the slope relative to scanline - how much scan coord changes per sweep change.
     Point lineDirAsPoint = end - start;
     return (lineDirAsPoint * scanDir.PerpDirectionAsPoint) / (lineDirAsPoint * scanDir.DirectionAsPoint);
 }
        internal static Point ScanLineIntersectSide(Point site, BasicObstacleSide side, ScanDirection scanDir)
        {
            // Note: we don't assert that site and side are not PointComparer.Equal, because ScanLine calls
            // this on sides that share vertices.
            Debug.Assert(!scanDir.IsFlat(side), "flat sides should not be in the scanline or encountered on lookahead scan");

            // We know that we will have an intersection if the side is adjacent in the scanline, so
            // we can optimize the calculation to project along the slope of the BasicObstacleSide.
            // Also, due to rounding, we need to make sure that when intersecting the side, we're not
            // falling short due to rounding error; that can be a problem if we're right at a vertex
            // of that obstacle, because then there is no intersection with the perpendicular line
            // from that vertex.  So make sure we are at least to the nearest coordinate of that side.

            // Note:  Calculate slope here using 'dir' rather than side.SlopeInverse because Reflection
            // lookaheads calculate the perpendicular intersection and side.Slope(Inverse) is always
            // relative to the scanline parallel.
            Point dir = side.Direction;
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369
            Point intersect = side.Start.Clone();
#else
            Point intersect = side.Start;
#endif
            if (scanDir.IsHorizontal) {
                intersect.X += (dir.X / dir.Y) * (site.Y - side.Start.Y);
                intersect.X = SpliceUtility.MungeIntersect(site.X, intersect.X, side.Start.X, side.End.X);
                intersect.Y = site.Y;
            }
            else {
                intersect.X = site.X;
                intersect.Y += (dir.Y / dir.X) * (site.X - side.Start.X);
                intersect.Y = SpliceUtility.MungeIntersect(site.Y, intersect.Y, side.Start.Y, side.End.Y);
            }
            return intersect;
        }
 internal static Obstacle CreateSentinel(Point a, Point b, ScanDirection scanDir, int scanlineOrdinal) {
     var sentinel = new Obstacle(a, b, scanlineOrdinal);
     sentinel.CreateInitialSides(sentinel.PaddedPolyline.StartPoint, scanDir);
     return sentinel;
 }
 // Set the initial ActiveLowSide and ActiveHighSide of the obstacle starting at this point.
 internal void CreateInitialSides(PolylinePoint startPoint, ScanDirection scanDir) {
     Debug.Assert((null == ActiveLowSide) && (null == ActiveHighSide)
                  , "Cannot call SetInitialSides when sides are already set");
     ActiveLowSide = new LowObstacleSide(this, startPoint, scanDir);
     ActiveHighSide = new HighObstacleSide(this, startPoint, scanDir);
     if (scanDir.IsFlat(ActiveHighSide)) {
         // No flat sides in the scanline; we'll do lookahead processing in the scanline to handle overlaps
         // with existing segments, and normal neighbor handling will take care of collinear OpenVertexEvents.
         ActiveHighSide = new HighObstacleSide(this, ActiveHighSide.EndVertex, scanDir);
     }
 }
        // 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;
        }