private void HandleGroupCrossing(Point site, BasicObstacleSide groupSide)
        {
            if (!base.ScanLineCrossesObstacle(site, groupSide.Obstacle))
            {
                return;
            }

            // Here we are always going left-to-right.  As in base.SkipToNeighbor, we don't stop traversal for groups,
            // neither do we create overlapped edges (unless we're inside a non-group obstacle).  Instead we turn
            // the boundary crossing on or off based on group membership at ShortestPath-time.  Even though this is
            // the sparse VG, we always create these edges at group boundaries so we don't skip over them.
            Directions dirToInsideOfGroup = (groupSide is LowObstacleSide) ? base.ScanDirection.Direction : base.ScanDirection.OppositeDirection;
            var        intersect          = this.ScanLineIntersectSide(site, groupSide);
            var        crossing           = base.CurrentGroupBoundaryCrossingMap.AddIntersection(intersect, groupSide.Obstacle, dirToInsideOfGroup);

            // The vertex crossing the edge is perpendicular to the group boundary.  A rectilinear group will also have
            // an edge parallel to that group boundary that includes the point of that crossing vertex; therefore we must
            // split that non-crossing edge at that vertex.
            AddPerpendicularCoordForGroupCrossing(intersect);

            // Similarly, the crossing edge's opposite vertex may be on a perpendicular segment.
            var interiorPoint = crossing.GetInteriorVertexPoint(intersect);

            AddPerpendicularCoordForGroupCrossing(interiorPoint);
        }
 internal void Clear() {
     this.LowNeighbor = null;
     this.LowOverlapEnd = null;
     this.GroupSideInterveningBeforeLowNeighbor = null;
     this.HighNeighbor = null;
     this.HighOverlapEnd = null;
     this.GroupSideInterveningBeforeHighNeighbor = null;
 }
예제 #3
0
        protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
                                                                BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action)
        {
            Debug.Assert(false, "base.wantReflections is false in Sparse mode so this should never be called");
// ReSharper disable HeuristicUnreachableCode
            return(false);
// ReSharper restore HeuristicUnreachableCode
        }
예제 #4
0
 internal void Clear()
 {
     this.LowNeighbor   = null;
     this.LowOverlapEnd = null;
     this.GroupSideInterveningBeforeLowNeighbor = null;
     this.HighNeighbor   = null;
     this.HighOverlapEnd = null;
     this.GroupSideInterveningBeforeHighNeighbor = null;
 }
 protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
         BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action) {
     // See notes in InsertPerpendicularReflectionSegment for comments about an existing segment.
     // Here, we call AddSegment which adds the segment and continues the reflection staircase.
     if (null != ParallelScanSegments.Find(start, end)) {
         return false;
     }
     return AddSegment(start, end, eventObstacle, lowNborSide, highNborSide, action, ScanSegment.ReflectionWeight);
 }
예제 #6
0
        private Point CreateScanSegment(Point start, BasicObstacleSide side, double weight)
        {
            var end = ScanLineIntersectSide(start, side);

            if (start != end)
            {
                this.parallelSegmentVector.CreateScanSegment(start, end, weight, CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(start, end));
            }
            return(end);
        }
 protected override bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
                                                         BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action)
 {
     // See notes in InsertPerpendicularReflectionSegment for comments about an existing segment.
     // Here, we call AddSegment which adds the segment and continues the reflection staircase.
     if (null != ParallelScanSegments.Find(start, end))
     {
         return(false);
     }
     return(AddSegment(start, end, eventObstacle, lowNborSide, highNborSide, action, ScanSegment.ReflectionWeight));
 }
 internal void SetSides(Directions dir, RBNode<BasicObstacleSide> neighborNode, RBNode<BasicObstacleSide> overlapEndNode,
                         BasicObstacleSide interveningGroupSide) {
     if (StaticGraphUtility.IsAscending(dir)) {
         HighNeighbor = neighborNode;
         HighOverlapEnd = overlapEndNode;
         this.GroupSideInterveningBeforeHighNeighbor = interveningGroupSide;
         return;
     }
     LowNeighbor = neighborNode;
     LowOverlapEnd = overlapEndNode;
     this.GroupSideInterveningBeforeLowNeighbor = interveningGroupSide;
 }
예제 #9
0
 private void StoreLookaheadSite(Obstacle eventObstacle, BasicObstacleSide interveningGroupSide, BasicObstacleSide neighborSide, Point siteOnSide)
 {
     // For reflections, NeighborSides won't be set, so there won't be an intervening group.  Otherwise,
     // this is on an OpenVertexEvent, so we'll either reflect of the intervening group if any, or neighborSide.
     if (null == interveningGroupSide)
     {
         this.StoreLookaheadSite(eventObstacle, neighborSide, siteOnSide, wantExtreme: false);
     }
     else
     {
         var siteOnGroup = ScanLineIntersectSide(siteOnSide, interveningGroupSide, this.ScanDirection);
         this.StoreLookaheadSite(eventObstacle, interveningGroupSide, siteOnGroup, wantExtreme: false);
     }
 }
예제 #10
0
 internal void SetSides(Directions dir, RBNode <BasicObstacleSide> neighborNode, RBNode <BasicObstacleSide> overlapEndNode,
                        BasicObstacleSide interveningGroupSide)
 {
     if (StaticGraphUtility.IsAscending(dir))
     {
         HighNeighbor   = neighborNode;
         HighOverlapEnd = overlapEndNode;
         this.GroupSideInterveningBeforeHighNeighbor = interveningGroupSide;
         return;
     }
     LowNeighbor   = neighborNode;
     LowOverlapEnd = overlapEndNode;
     this.GroupSideInterveningBeforeLowNeighbor = interveningGroupSide;
 }
 // obstacleToIgnore is the event obstacle if we're looking at intersections along its boundary.
 private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, Obstacle eventObstacle, Point intersect)
 {
     // See if the intersection with an obstacle side is inside another obstacle (that encloses
     // at least the part of side.Obstacle containing the intersection).  This will only happen
     // if side.Obstacle is overlapped and in the same clump (if it's not the same clump, we must
     // be hitting it from the outside).
     if (!side.Obstacle.IsOverlapped)
     {
         return(false);
     }
     if (!side.Obstacle.IsGroup && !eventObstacle.IsGroup && (side.Obstacle.Clump != eventObstacle.Clump))
     {
         return(false);
     }
     return(ObstacleTree.IntersectionIsInsideAnotherObstacle(side.Obstacle, eventObstacle, intersect, ScanDirection));
 }
예제 #12
0
        private bool SkipSide(Point start, BasicObstacleSide side)
        {
            if (side.Obstacle.IsSentinel)
            {
                return(true);
            }

            // Skip sides of obstacles that we do not actually pass through.
            var bbox = side.Obstacle.VisibilityBoundingBox;

            if (base.ScanDirection.IsHorizontal)
            {
                return((start.Y == bbox.Bottom) || (start.Y == bbox.Top));
            }
            return((start.X == bbox.Left) || (start.X == bbox.Right));
        }
        // Calculate reflections from the lines, depending on line side (Low vs. High) and slope.
        // Because the low neighbor intersection is on a high side of its obstacle
        // and vice-versa, then the "side" of a lowNbor is a highSide, and vice versa.
        protected void StoreLookaheadSite(Obstacle initialObstacle, BasicObstacleSide reflectingSide, Point reflectionSite, bool wantExtreme) {
            if (!this.wantReflections) {
                return;
            }

            // If the line is perpendicular, we won't generate reflections (they'd be redundant).
            if (!IsPerpendicular(reflectingSide)) {
                // If this is hitting an extreme vertex in the forward direction, we will (or already did) create a normal
                // ScanSegment in the perpendicular direction.
                if (!wantExtreme && !StaticGraphUtility.PointIsInRectangleInterior(reflectionSite, reflectingSide.Obstacle.VisibilityBoundingBox)) {
                    return;
                }

                // We can only do upward reflections, which fortunately is all we need.
                if (SideReflectsUpward(reflectingSide)) {
                    // We defer actually creating the perpendicular line until we've processed
                    // the reflection event we're about to enqueue, so we may legitimately encounter
                    // two (or more) reflections at the same point along the parallel-to-scanline
                    // coordinate, if we have a side that is nearly but not quite perpendicular to
                    // the scanline.  For example:
                    //      \
                    //       \----------------------------- line2
                    //        \---------------------------- line1
                    // Assume the vertical side is very close to perpendicular; then as we process
                    // the horizontal lines in the upward direction, we may generate two lookahead
                    // reflections at coordinates that are sufficiently far apart in the vertical
                    // coordinate (in this example, Y) that the lines are not subsumed, but are
                    // sufficiently close in the horizontal coordinate (in this example, X) that
                    // the perpendicular lines would be subsumed. In that case, we look here to see
                    // if there is an active lookahead scan for the reflecting site's parallel coordinate;
                    // if so, then because we know that the side reflects upward, we also know that
                    // the perpendicular line from the lowest segment's reflection site will intersect
                    // the higher segments here, providing the VisibilityVertices we need, so we can
                    // safely ignore the duplicate lookahead.

                    // Don't worry about enqueueing a reflection at the extreme scanline-parallel
                    // vertex because we'll MergeSegments to handle that.
                    if (null == lookaheadScan.Find(reflectionSite)) {
                        lookaheadScan.Add(new BasicReflectionEvent(initialObstacle, reflectingSide.Obstacle, reflectionSite));
                        DevTraceInfoVgGen(1, "Storing reflection lookahead site {0}", reflectionSite);
                    }
                    else {
                        DevTraceInfoVgGen(1, "Reflection lookahead site {0} already exists", reflectionSite);
                    }
                }
            }
        } // end StoreLookaheadSite()
 bool SideReflectsDownward(BasicObstacleSide side) {
     // Returns false if vertical.
     if (side is LowObstacleSide) {
         // Low side slopes downward if slope is negative (to the low direction).
         return ScanDirection.Coord(side.End) < ScanDirection.Coord(side.Start);
     }
     // High side slopes downward if slope is positive (to the high direction).
     return ScanDirection.Coord(side.End) > ScanDirection.Coord(side.Start);
 }
 // obstacleToIgnore is the event obstacle if we're looking at intersections along its boundary.
 private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, Obstacle eventObstacle, Point intersect) {
     // See if the intersection with an obstacle side is inside another obstacle (that encloses
     // at least the part of side.Obstacle containing the intersection).  This will only happen
     // if side.Obstacle is overlapped and in the same clump (if it's not the same clump, we must
     // be hitting it from the outside).
     if (!side.Obstacle.IsOverlapped) {
         return false;
     }
     if (!side.Obstacle.IsGroup && !eventObstacle.IsGroup && (side.Obstacle.Clump != eventObstacle.Clump)) {
         return false;
     }
     return ObstacleTree.IntersectionIsInsideAnotherObstacle(side.Obstacle, eventObstacle, intersect, ScanDirection);
 }
        } // end ProcessEvent(LowBendVertexEvent)

        void EnqueueLowBendVertexEvent(BasicObstacleSide lowSide) {
            // We've already ensured the extension is valid so just queue the next event.
            eventQueue.Enqueue(new LowBendVertexEvent(lowSide.Obstacle, lowSide.EndVertex));
        }
        RBNode<BasicObstacleSide> AddSideToScanLine(BasicObstacleSide side, Point scanPos) {
            RBNode<BasicObstacleSide> node = scanLine.Insert(side, scanPos);

            // Now get any pending LookaheadScan intersections along this side.
            LoadReflectionEvents(side);
            return node;
        }
 protected abstract bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle,
         BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action);
        // Determine whether the event is valid and do some common processing.
        bool AddPerpendicularReflectionSegment(BasicReflectionEvent currentEvent, BasicObstacleSide eventSide, BasicObstacleSide nborSide) {
            // If eventSide is null it means we had the wrong side type as a scanline neighbor.
            // If another obstacle opened up, then that obstacle (or another intervening one) should have
            // drained this reflection event.
            Debug.Assert(null != eventSide, "eventSide should not be null");

            // Between the time currentEvent was queued and the time we're now processing it, another
            // obstacle may have opened between the previousSite and the eventSite, in which case it
            // removed currentEvent from the queue already.  So currentEvent may be stale.  The new
            // obstacle may have stored *another* lookahead site with the same scanline-parallel 
            // coordinate (but higher up perpendicularly).  So remove the exact site of currentEvent; 
            // otherwise the currentEvent could be a stale event with the lower scanline-parallel
            // coordinate, and would remove the site from the lookahead list before the "live" event
            // looks for it.  See RectilinearTests.ReflectionsRemoveInterceptedSite.
            if (lookaheadScan.RemoveExact(currentEvent.PreviousSite)) {
                Debug.Assert(currentEvent.InitialObstacle == currentEvent.PreviousSite.ReflectingObstacle
                            , "Inconsistency: currentEvent.InitialObstacle != currentEvent.PreviousSite.ReflectingObstacle");
// ReSharper disable HeuristicUnreachableCode
// ReSharper disable ConditionIsAlwaysTrueOrFalse
                if (null == eventSide) {
                    // We've removed the event so there's nothing else to do.
                    return false;
                }
// ReSharper restore ConditionIsAlwaysTrueOrFalse
// ReSharper restore HeuristicUnreachableCode

                // If the two sides intersect ahead of the scanline, we don't want the reflection.

                // If the reflecting side is flat, no reflection is done - that's handled by OpenVertexEvent.
                Debug.Assert(!IsFlat(eventSide), "Flat sides should not be encountered in reflections");
                if (currentEvent.PreviousSite.IsStaircaseStep(currentEvent.ReflectingObstacle)) {
                    // We need to draw the perpendicular lines here because we may be on the second
                    // sweep so there won't be a subsequent sweep to draw them.  And if we're on the
                    // second sweep, we may have already loaded this segment as part of a continuation
                    // of an overlapped segment. Either way, we only want this if we are not on an extreme
                    // edge of the target obstacle (reflectingObstacle for the perpendicular segment,
                    // nborSide.Obstacle for the parallel segment).  Extreme vertices will generate segments.
                    // See TestRectilinear.Reflection_Staircase_Stops_At_BoundingBox_Side*.
                    if (!StaticGraphUtility.PointIsInRectangleInterior(currentEvent.Site, currentEvent.ReflectingObstacle.VisibilityBoundingBox))
                    {
                        return false;
                    }
                    DevTraceInfoVgGen(1, "Perpendicular Reflection - Adding Segment [{0} -> {1}]", currentEvent.PreviousSite.Site, currentEvent.Site);
                    DevTraceInfoVgGen(2, "  -> side {0}", eventSide);   // same indent as AddSegment; eventSide is highNbor
                    if (!InsertPerpendicularReflectionSegment(currentEvent.PreviousSite.Site, currentEvent.Site)) {
                        return false;
                    }

                    // If the neighbor continues the staircase and the parallel segment would hit a non-extreme point
                    // on the neighbor, return true and the Low/HighReflectionEvent handler will add the parallel segment.
                    if ((null != nborSide) && currentEvent.IsStaircaseStep(nborSide.Obstacle)) {
                        return this.ScanLineCrossesObstacle(currentEvent.Site, nborSide.Obstacle);
                    }
                    DevTraceInfoVgGen(1, "Reflection Lookahead site {0} is not an outgoing staircase step; discontinuing", currentEvent.PreviousSite);
                }
                else {
                    DevTraceInfoVgGen(1, "Reflection Lookahead site {0} is not an incoming staircase step; discontinuing", currentEvent.PreviousSite);
                }
            }
            else {
                DevTraceInfoVgGen(1, "Reflection Lookahead site {0} is no longer in the lookahead table; skipping", currentEvent.PreviousSite);
            }
            return false;
        }
예제 #20
0
        private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, BasicVertexEvent vertexEvent)
        {
            Point intersect = ScanLineIntersectSide(vertexEvent.Site, side);

            return(IntersectionAtSideIsInsideAnotherObstacle(side, vertexEvent.Obstacle, intersect));
        }
예제 #21
0
        // As described in the document, we currently don't create ScanSegments where a flat top/bottom boundary may have
        // intervals that are embedded within overlapped segments:
        //      obstacle1 |  |obstacle2  | obstacle3 |          | obstacle4 | obstacle2|  |
        //                |  +-----------|===========|??????????|===========|----------+  | obstacle5
        //   ...__________|              |___________|          |___________|             |__________...
        // Here, there will be no ScanSegment created at ??? along the border of obstacle2 between obstacle3
        // and obstacle4.  This is not a concern because that segment is useless anyway; a path from outside
        // obstacle2 will not be able to see it unless there is another obstacle in that gap, and then that
        // obstacle's side-derived ScanSegments will create non-overlapped edges; and there are already edges
        // from the upper/lower extreme vertices of obstacle 3 and obstacle4 to ensure a connected graph.
        // If there is a routing from an obstacle outside obstacle2 to one embedded within obstacle2, the
        // port visibility will create the necessary edges.
        //
        // We don't try to determine nesting depth and create different VisibilityEdge weights to prevent spurious
        // nested-obstacle crossings; we just care about overlapped vs. not-overlapped.
        // If this changes, we would have to: Find the overlap depth at the lowNborSide intersection,
        // then increment/decrement according to side type as we move from low to high, then create a different
        // ScanSegment at each obstacle-side crossing, making ScanSegment.IsOverlapped a depth instead of bool.
        // Then pass that depth through to VisibilityEdge as an increased weight.  (This would also automatically
        // handle the foregoing situation of non-overlapped intervals in the middle of a flat top/bottom border,
        // not that it would really gain anything).
        void CreateScanSegments(Obstacle obstacle, HighObstacleSide lowNborSide, BasicObstacleSide lowOverlapSide,
                                BasicObstacleSide highOverlapSide, LowObstacleSide highNborSide, BasicVertexEvent vertexEvent)
        {
            // If we have either of the high/low OverlapSides, we'll need to see if they're inside
            // another obstacle.  If not, they end the overlap.
            if ((null == highOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(highOverlapSide, vertexEvent))
            {
                highOverlapSide = highNborSide;
            }

            if ((null == lowOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(lowOverlapSide, vertexEvent))
            {
                lowOverlapSide = lowNborSide;
            }

            // There may be up to 3 segments; for a simple diagram, |# means low-side border of
            // obstacle '#' and #| means high-side, with 'v' meaning our event vertex.  Here are
            // the two cases where we create a single non-overlapped ScanSegment (from the Low
            // side in the diagram, but the same logic works for the High side).
            //  - non-overlapped:   1|  v  |2
            //                 ...---+     +---...
            //  - non-overlapped to an "inner" highNbor on a flat border:   1|  vLow  |2   vHigh
            //                                                          ...---+  +-----+=========...
            // This may be the low side of a flat bottom or top border, so lowNbor or highNbor
            // may be in the middle of the border.
            Point lowNborIntersect            = ScanLineIntersectSide(vertexEvent.Site, lowNborSide);
            Point highNborIntersect           = ScanLineIntersectSide(vertexEvent.Site, highNborSide);
            bool  lowNborEndpointIsOverlapped = IntersectionAtSideIsInsideAnotherObstacle(lowNborSide,
                                                                                          vertexEvent.Obstacle /*obstacleToIgnore*/, lowNborIntersect);

            if (!lowNborEndpointIsOverlapped && (lowNborSide == lowOverlapSide))
            {
                // Nothing is overlapped so create one segment.
                AddSegment(lowNborIntersect, highNborIntersect, obstacle, lowNborSide, highNborSide, vertexEvent,
                           ScanSegment.NormalWeight);
                return;
            }

            // Here are the different interval combinations for overlapped cases.
            //  - non-overlapped, overlapped:  1|  |2  v  |3
            //                            ...---+  +------+===...
            //  - non-overlapped, overlapped, non-overlapped:  1|  |2  v  2|  |3
            //                                                ==+  +-------+  +--...
            //  - overlapped:   |1  2|  v  |3  ...1|
            //            ...---+====+-----+===...-+
            //  - overlapped, non-overlapped:  |1  2|  v  1|  |3
            //                           ...---+====+------+  +---...
            // We will not start overlapped and then go to non-overlapped and back to overlapped,
            // because we would have found the overlap-ending/beginning sides as nearer neighbors.

            // Get the other intersections we'll want.
            Point highOverlapIntersect = (highOverlapSide == highNborSide) ? highNborIntersect
                                            : ScanLineIntersectSide(vertexEvent.Site, highOverlapSide);
            Point lowOverlapIntersect = (lowOverlapSide == lowNborSide) ? lowNborIntersect
                                            : ScanLineIntersectSide(vertexEvent.Site, lowOverlapSide);

            // Create the segments.
            if (!lowNborEndpointIsOverlapped)
            {
                // First interval is non-overlapped; there is a second overlapped interval, and may be a
                // third non-overlapping interval if another obstacle surrounded this vertex.
                AddSegment(lowNborIntersect, lowOverlapIntersect, obstacle, lowNborSide, lowOverlapSide, vertexEvent,
                           ScanSegment.NormalWeight);
                AddSegment(lowOverlapIntersect, highOverlapIntersect, obstacle, lowOverlapSide, highOverlapSide, vertexEvent,
                           ScanSegment.OverlappedWeight);
                if (highOverlapSide != highNborSide)
                {
                    AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent,
                               ScanSegment.NormalWeight);
                }
            }
            else
            {
                // Starts off overlapped so ignore lowOverlapSide.
                AddSegment(lowNborIntersect, highOverlapIntersect, obstacle, lowNborSide, highOverlapSide, vertexEvent,
                           ScanSegment.OverlappedWeight);
                if (highOverlapSide != highNborSide)
                {
                    AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent,
                               ScanSegment.NormalWeight);
                }
            }
        }
 private void StoreLookaheadSite(Obstacle eventObstacle, BasicObstacleSide interveningGroupSide, BasicObstacleSide neighborSide, Point siteOnSide) {
     // For reflections, NeighborSides won't be set, so there won't be an intervening group.  Otherwise,
     // this is on an OpenVertexEvent, so we'll either reflect of the intervening group if any, or neighborSide.
     if (null == interveningGroupSide) {
         this.StoreLookaheadSite(eventObstacle, neighborSide, siteOnSide, wantExtreme: false);
     } else {
         var siteOnGroup = ScanLineIntersectSide(siteOnSide, interveningGroupSide, this.ScanDirection);
         this.StoreLookaheadSite(eventObstacle, interveningGroupSide, siteOnGroup, wantExtreme: false);
     }
 }
 private bool IntersectionAtSideIsInsideAnotherObstacle(BasicObstacleSide side, BasicVertexEvent vertexEvent) {
     Point intersect = ScanLineIntersectSide(vertexEvent.Site, side);
     return IntersectionAtSideIsInsideAnotherObstacle(side, vertexEvent.Obstacle, intersect);
 }
        // 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;
        }
        } // end StoreLookaheadSite()

        // Load any lookahead scan ray intersections with a side we've just added.
        protected void LoadReflectionEvents(BasicObstacleSide sideToQueue) {
            LoadReflectionEvents(sideToQueue, sideToQueue);
        }
예제 #26
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);
        }
        // sideWithRange is either the same as sideToQueue, if that side is being loaded by an
        // OpenVertexEvent, or is a different side that is just closing.
        protected void LoadReflectionEvents(BasicObstacleSide sideToQueue, BasicObstacleSide sideWithRange) {
            // If this line reflects upward then it cannot receive rays from below (they would pass
            // through its obstacle), and of course a perpendicular lookahead line will never
            // intersect a perpendicular side.
            if ((null == sideToQueue) || SideReflectsUpward(sideToQueue) || IsPerpendicular(sideToQueue)) {
                return;
            }

            // If there is no overlap in the rectangles along the current axis, there is nothing
            // to do.  This reduces (but doesn't prevent) duplicate events being loaded.
            var bbox1 = new Rectangle(sideToQueue.Start, sideToQueue.End);
            var bbox2 = new Rectangle(sideWithRange.Start, sideWithRange.End);
            if ((ScanDirection.IsHorizontal) ? !bbox1.IntersectsOnX(bbox2) : !bbox1.IntersectsOnY(bbox2)) {
                return;
            }

            // Make sure we order the endpoints from low to high along the scanline parallel, and get only
            // the intersection.  RectilinearFileTests.Nudger_Overlap* exercise reflection lookahead subranges.
            Rectangle bboxIntersect = Rectangle.Intersect(bbox1, bbox2);
            Point low = bboxIntersect.LeftBottom;
            Point high = bboxIntersect.RightTop;

            // This is inclusive of the endpoints of sideWithRange, to be sure that we remove the item
            // from LookaheadScan; if it's on an extreme vertex in the perpendicular sweep then it will
            // stop the chain; see TestRectilinear.Reflection_Staircase_Stops_At_BoundingBox_Side*.
            RBNode<BasicReflectionEvent> lookaheadSiteNode = lookaheadScan.FindFirstInRange(low, high);
            while (null != lookaheadSiteNode) {
                // Calculate the lookahead intersection with this side in the perpendicular direction to
                // the scanline.  Note: due to rounding error, this may be different from the calculation
                // in the parallel direction when the scanline gets up to the ScanDirection.PerpCoord(intersect);
                // this will be adjusted in ScanSegmentTree.MergeSegments.
                Point intersect = ScanLineIntersectSide(lookaheadSiteNode.Item.Site, sideToQueue
                            , ScanDirection.PerpendicularInstance);
                DevTraceInfoVgGen(1, "Loading reflection from lookahead site {0} to intersect at {1}"
                            , lookaheadSiteNode.Item.Site, intersect);
                DevTraceInfoVgGen(2, "     side {0})", sideToQueue);    // same indent as AddSegment

                // In some cases where the ActiveLowSide and ActiveHighSide of an obstacle lean in the same
                // direction such that LowSide is above HighSide, e.g. on the horizontal pass when they both
                // lean to the right, the delayed-lookahead in CloseVertex (obstacle close) event may find the
                // high side spanning the scanline-parallel coordinate range where its low side has enqueued
                // lookahead events.  In that case the intersection will be less than the enqueueing site so
                // ignore it.  See RectilinearTests.ReflectionsSitedByLowSideAreNotLoadedByHighSide.)
                // Similarly, if this is at the same perpendicular coordinate as the current scanline
                // position, ignore it; otherwise we could back up in the scanline's parallel coordinate.
                // Since we retrieved events for the endpoint of any previous side, this won't be
                // encountered on a bend vertex event; therefore we're on a near-flat bottom side
                // so we're parallel to the extreme-vertex line and it's fine to just absorb the photon.
                // This also handles the case of reflections into intersecting sides - at some point
                // they converge such that the intersection is not ahead of the lookahead site.
                if (ScanDirection.ComparePerpCoord(intersect, lookaheadSiteNode.Item.Site) > 0) {
                    // Add an event to continue the chain, "shifting" the site's reflecting
                    // obstacle back to the initialObstacle position.  We must load this here 
                    // and process it in ConfirmLookaheadEvent so it will be removed from
                    // the lookahead list; we can't remove it here if it doesn't satisfy the
                    // staircase requirements, because this may be called from loading a "higher"
                    // side (during the sweep) which could steal events from the lower side.
                    AddReflectionEvent(lookaheadSiteNode.Item, sideToQueue, intersect);
                }
                else {
                    if (lookaheadSiteNode.Item.ReflectingObstacle != sideToQueue.Obstacle) {
                        DevTraceInfoVgGen(1, "  (discarding reflection at intersect {0} as it is not ahead of the previous site)", intersect);

                        // We need to remove the site.  We're in the middle of Node enumeration so just
                        // mark the site and on function exit we'll remove any so marked.
                        lookaheadScan.MarkStaleSite(lookaheadSiteNode.Item);
                    }
                    else {
                        DevTraceInfoVgGen(1, "  (skipping reflection at intersect {0} as it is the same obstacle)", intersect);
                    }
                }

                // Get the next item, leaving the current one in the lookahead scan until
                // we actually process the event; this lets us know whether an intervening
                // obstacle may be opened and intercepted the reflection. ConfirmLookaheadEvents
                // will actually do the removal when the lowest side containing the lookahead
                // site is loaded.  See RectilinearTests.ReflectionsRemoveInterceptedSite.
                lookaheadSiteNode = lookaheadScan.FindNextInRange(lookaheadSiteNode, high);
            } // endwhile previousSiteNode

            lookaheadScan.RemoveStaleSites();
        }
        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;
        }
 private bool AddParallelReflectionSegment(Obstacle eventObstacle, BasicObstacleSide lowNborSide
                         , BasicObstacleSide highNborSide, BasicReflectionEvent action) {
     // If this is reflecting to a low neighbor, then that intersect is 'start' in the low-to-high
     // sequence, and the event site is the end; otherwise we start at the event site and end at
     // the high neighbor.
     Point intersect = ScanLineIntersectSide(action.Site, lowNborSide ?? highNborSide);
     Point start = (null != lowNborSide) ? intersect : action.Site;
     Point end = (null != lowNborSide) ? action.Site : intersect;
     
     // Now get the opposite neighbors so AddSegment can continue the reflection chain.
     if (null == lowNborSide) {
         lowNborSide = scanLine.NextLow(highNborSide).Item;
     }
     else {
         highNborSide = scanLine.NextHigh(lowNborSide).Item;
     }
     return InsertParallelReflectionSegment(start, end, eventObstacle, lowNborSide, highNborSide, action);
 }
        } // end EnqueueBottomVertexEvents

        internal bool IsFlat(BasicObstacleSide side) {
            return ScanDirection.IsFlat(side);
        }
        void AddReflectionEvent(BasicReflectionEvent previousSite, BasicObstacleSide side, Point site) {
            Debug.Assert(null != scanLine.Find(side), "AddReflectionEvent could not find 'side' in the scanline");

            // Add an event that will be drained when a side spanning the scanline-parallel is loaded
            // as the sweep moves "up".
            var lowSide = side as LowObstacleSide;
            if (lowSide != null) {
                eventQueue.Enqueue(new LowReflectionEvent(previousSite, lowSide, site));
            } else {
                eventQueue.Enqueue(new HighReflectionEvent(previousSite, (HighObstacleSide)side, site));
            }
        }
 internal bool IsPerpendicular(BasicObstacleSide side) {
     // If it's perpendicular we won't generate reflections.
     return ScanDirection.IsPerpendicular(side);
 }
        void SkipToNeighbor(Directions nborSearchDir, BasicObstacleSide side, Point sideReferencePoint,
                            RBNode<BasicObstacleSide> nborNode, NeighborSides neighborSides) {
            // Find the first neighbor side (LowObstacleSide if going high, HighObstacleSide if going low) and
            // the side of opposite type (which would potentially end overlap), that that we cross *through*, if any.
            RBNode<BasicObstacleSide> overlapSideNode = null;
            BasicObstacleSide interveningGroupSide = null;
            for (; ; nborNode = scanLine.Next(nborSearchDir, nborNode)) {
                // Ignore the opposite side of the current obstacle.
                if (nborNode.Item.Obstacle == side.Obstacle) {
                    continue;
                }

                if (nborNode.Item.Obstacle.IsGroup) {
                    if (ProcessGroupSideEncounteredOnTraversalToNeighbor(nborNode, sideReferencePoint, nborSearchDir)) {
                        // Keep the first one (outermost) encountered.
                        if (null == interveningGroupSide) {
                            interveningGroupSide = nborNode.Item;
                        }
                    }
                    continue;
                }

                // Check for overlap-ending obstacle.
                if ((nborNode.Item is HighObstacleSide) == StaticGraphUtility.IsAscending(nborSearchDir)) {
                    if (ScanLineCrossesObstacle(sideReferencePoint, nborNode.Item.Obstacle)) {
                        overlapSideNode = nborNode;
                        interveningGroupSide = null;
                    }
                    continue;
                }

                // If we're here, we found the neighbor we were looking for.
                break;
            }

            neighborSides.SetSides(nborSearchDir, nborNode, overlapSideNode, interveningGroupSide);
        }
 // Params are event site (vertex point) and the obstacle side adjacent to that site.
 protected Point ScanLineIntersectSide(Point site, BasicObstacleSide side) {
     return ScanLineIntersectSide(site, side, ScanDirection);
 }
 void EnqueueHighBendOrCloseVertexEvent(BasicObstacleSide highSide) {
     // If the next side segment after highSide is ascending from the scanline we want to queue another 
     // HighBendVertexEvent; otherwise it is flat or turns down toward the scanline so queue a CloseVertexEvent.
     Obstacle obstacle = highSide.Obstacle;
     PolylinePoint nextHighSideEnd = ScanDirection.IsHorizontal  // Traverse clockwise or counterclockwise
                                 ? highSide.EndVertex.PrevOnPolyline 
                                 : highSide.EndVertex.NextOnPolyline;
     if (ScanDirection.ComparePerpCoord(nextHighSideEnd.Point, highSide.End) > 0) {
         eventQueue.Enqueue(new HighBendVertexEvent(obstacle, highSide.EndVertex));
     }
     else {
         eventQueue.Enqueue(new CloseVertexEvent(obstacle, highSide.EndVertex));
     }
 } // end ProcessEvent(HighBendVertexEvent)
        // As described in the document, we currently don't create ScanSegments where a flat top/bottom boundary may have
        // intervals that are embedded within overlapped segments:
        //      obstacle1 |  |obstacle2  | obstacle3 |          | obstacle4 | obstacle2|  |
        //                |  +-----------|===========|??????????|===========|----------+  | obstacle5
        //   ...__________|              |___________|          |___________|             |__________...
        // Here, there will be no ScanSegment created at ??? along the border of obstacle2 between obstacle3
        // and obstacle4.  This is not a concern because that segment is useless anyway; a path from outside
        // obstacle2 will not be able to see it unless there is another obstacle in that gap, and then that
        // obstacle's side-derived ScanSegments will create non-overlapped edges; and there are already edges
        // from the upper/lower extreme vertices of obstacle 3 and obstacle4 to ensure a connected graph.
        // If there is a routing from an obstacle outside obstacle2 to one embedded within obstacle2, the
        // port visibility will create the necessary edges.
        //
        // We don't try to determine nesting depth and create different VisibilityEdge weights to prevent spurious
        // nested-obstacle crossings; we just care about overlapped vs. not-overlapped.
        // If this changes, we would have to: Find the overlap depth at the lowNborSide intersection,
        // then increment/decrement according to side type as we move from low to high, then create a different
        // ScanSegment at each obstacle-side crossing, making ScanSegment.IsOverlapped a depth instead of bool.
        // Then pass that depth through to VisibilityEdge as an increased weight.  (This would also automatically
        // handle the foregoing situation of non-overlapped intervals in the middle of a flat top/bottom border,
        // not that it would really gain anything).
        void CreateScanSegments(Obstacle obstacle, HighObstacleSide lowNborSide, BasicObstacleSide lowOverlapSide, 
                                BasicObstacleSide highOverlapSide, LowObstacleSide highNborSide, BasicVertexEvent vertexEvent) {
            // If we have either of the high/low OverlapSides, we'll need to see if they're inside
            // another obstacle.  If not, they end the overlap.
            if ((null == highOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(highOverlapSide, vertexEvent)) {
                highOverlapSide = highNborSide;
            }
            if ((null == lowOverlapSide) || IntersectionAtSideIsInsideAnotherObstacle(lowOverlapSide, vertexEvent)) {
                lowOverlapSide = lowNborSide;
            }

            // There may be up to 3 segments; for a simple diagram, |# means low-side border of
            // obstacle '#' and #| means high-side, with 'v' meaning our event vertex.  Here are
            // the two cases where we create a single non-overlapped ScanSegment (from the Low
            // side in the diagram, but the same logic works for the High side).
            //  - non-overlapped:   1|  v  |2
            //                 ...---+     +---...
            //  - non-overlapped to an "inner" highNbor on a flat border:   1|  vLow  |2   vHigh
            //                                                          ...---+  +-----+=========...
            // This may be the low side of a flat bottom or top border, so lowNbor or highNbor 
            // may be in the middle of the border.
            Point lowNborIntersect = ScanLineIntersectSide(vertexEvent.Site, lowNborSide);
            Point highNborIntersect = ScanLineIntersectSide(vertexEvent.Site, highNborSide);
            bool lowNborEndpointIsOverlapped = IntersectionAtSideIsInsideAnotherObstacle(lowNborSide,
                        vertexEvent.Obstacle /*obstacleToIgnore*/, lowNborIntersect);

            if (!lowNborEndpointIsOverlapped && (lowNborSide == lowOverlapSide)) {
                // Nothing is overlapped so create one segment.
                AddSegment(lowNborIntersect, highNborIntersect, obstacle, lowNborSide, highNborSide, vertexEvent,
                        ScanSegment.NormalWeight);
                return;
            }

            // Here are the different interval combinations for overlapped cases.
            //  - non-overlapped, overlapped:  1|  |2  v  |3
            //                            ...---+  +------+===...
            //  - non-overlapped, overlapped, non-overlapped:  1|  |2  v  2|  |3
            //                                                ==+  +-------+  +--...
            //  - overlapped:   |1  2|  v  |3  ...1|
            //            ...---+====+-----+===...-+
            //  - overlapped, non-overlapped:  |1  2|  v  1|  |3
            //                           ...---+====+------+  +---...
            // We will not start overlapped and then go to non-overlapped and back to overlapped,
            // because we would have found the overlap-ending/beginning sides as nearer neighbors.

            // Get the other intersections we'll want.
            Point highOverlapIntersect = (highOverlapSide == highNborSide) ? highNborIntersect
                                            : ScanLineIntersectSide(vertexEvent.Site, highOverlapSide);
            Point lowOverlapIntersect = (lowOverlapSide == lowNborSide) ? lowNborIntersect
                                            : ScanLineIntersectSide(vertexEvent.Site, lowOverlapSide);

            // Create the segments.
            if (!lowNborEndpointIsOverlapped) {
                // First interval is non-overlapped; there is a second overlapped interval, and may be a 
                // third non-overlapping interval if another obstacle surrounded this vertex.
                AddSegment(lowNborIntersect, lowOverlapIntersect, obstacle, lowNborSide, lowOverlapSide, vertexEvent,
                        ScanSegment.NormalWeight);
                AddSegment(lowOverlapIntersect, highOverlapIntersect, obstacle, lowOverlapSide, highOverlapSide, vertexEvent,
                        ScanSegment.OverlappedWeight);
                if (highOverlapSide != highNborSide) {
                    AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent,
                        ScanSegment.NormalWeight);
                }
            }
            else {
                // Starts off overlapped so ignore lowOverlapSide.
                AddSegment(lowNborIntersect, highOverlapIntersect, obstacle, lowNborSide, highOverlapSide, vertexEvent, 
                        ScanSegment.OverlappedWeight);
                if (highOverlapSide != highNborSide) {
                    AddSegment(highOverlapIntersect, highNborIntersect, obstacle, highOverlapSide, highNborSide, vertexEvent,
                        ScanSegment.NormalWeight);
                }
            }
        }