void ProcessEvent(CloseVertexEvent closeVertEvent) { // This event closes the obstacle. It removes the ActiveLowSide and ActiveHighSide from the scanline and does // not add new sides. As above, see comments in OpenVertexEvent and its callees for more detailed explanations. CreateCloseEventSegmentsAndFindNeighbors(closeVertEvent); // For reflection, in addition to the delayed lookahead we do in the other *VertexEvents, we need to // detect pending reflections up to an already loaded obstacle side. This may be transitive; using // H directions as an example: // (A). Obstacles A and B lean rightward such that a vertical line can be drawn from the lower side // of ObstacleA downward to the right of ObstacleB (missing it) and hitting Obstacle C. // (B). ObstacleC's start point is above the start points of Obstacles A and B. // (C). ObjectC reflects a Lookahead scan up along the line cited in (A). // To handle this, whenever we close an object, see if either nbor side spans lookahead scan points. // If so, then add the events. Otherwise, we know that a new side for those nbor objects, or for // subsequent higher objects, will be created at some point (unless we run out of obstacles). // Since we do reflections only in staircase situations, these lookahead events will be discarded // (because the existing edge would have to have already been processed as an immediate neighbor, // in order to satisfy the definition of a staircase). See RectilinearTests.ReflectionsDetectedByAlreadyLoadedSide. // Check the neighbors in both directions for reflection events. // When querying for lookahead sites for a nborSide, always test the full nborSide range if // the opposite nborSide reflects upward, because we will have generated a lookahead site for the // current eventObstacle on that upward-reflecting nborSide. For example, restricting the // lowNborSide lookahead-site query to the currentEventObstacle.ActiveLowSide would pick up // any lookahead sites stored on eventObstacle.ActiveLowSide, but would not pick up a lookahead // site that the current CloseVertexEvent just stored on the highNborSide). // Fix: Updated this to remove restricted-range as it skips the range that includes the far side of the // current obstacle when called for neighbors that extend across the top vertex. var lowNborSide = (HighObstacleSide)LowNeighborSides.LowNeighbor.Item; var highNborSide = (LowObstacleSide)HighNeighborSides.HighNeighbor.Item; var obstacle = closeVertEvent.Obstacle; LoadReflectionEvents(lowNborSide); LoadReflectionEvents(highNborSide); // This prepares the object for the second (perpendicular) sweep. obstacle.Close(); } // end ProcessEvent(CloseVertexEvent)
} // end ProcessEvent(HighBendVertexEvent) void CreateCloseEventSegmentsAndFindNeighbors(CloseVertexEvent closeVertEvent) { var obstacle = closeVertEvent.Obstacle; DevTraceIfFlatSide(false /*isObstacleOpen*/, obstacle.ActiveLowSide.End, obstacle.ActiveHighSide.End); RBNode<BasicObstacleSide> lowSideNode = scanLine.Find(obstacle.ActiveLowSide); RBNode<BasicObstacleSide> highSideNode = scanLine.Find(obstacle.ActiveHighSide); // Two sides coming together at a top point will be reverse-ordered in the scanline, // because their projections ahead of the intersection are slope-based. This must // be the case in order to maintain scanline consistency. Therefore we need to // check the comparison and reverse the local variables if necessary. Fortunately // we only concern ourselves with the actual Low-vs-High side type for neighbors, // not the sides of the obstacle. if (1 == scanLine.Compare(obstacle.ActiveLowSide, obstacle.ActiveHighSide)) { var temp = lowSideNode; lowSideNode = highSideNode; highSideNode = temp; } // As with OpenVertexEvent, the idea here is to find the neighbors and draw the line between them // that includes the event vertex (and any flat top obstacle side), with consideration for overlaps. this.FindNeighborsAndProcessVertexEvent(lowSideNode, highSideNode, closeVertEvent); // Inner overlaps: any overlapped sides coming out of the obstacle must have reflection events // drained for any that were generated from sides of the closing obstacle if they extend // outside the closing obstacle. if (this.wantReflections && obstacle.IsOverlapped) { for (RBNode<BasicObstacleSide> nextNode = scanLine.NextHigh(lowSideNode); nextNode.Item != highSideNode.Item; nextNode = scanLine.NextHigh(nextNode)) { LoadReflectionEvents(nextNode.Item); } } // Remove the obstacle from the Scanline. This comes after the foregoing which is why it's a // separate function; the RBTree modifies RBNodes, so doing the .Remove at the end of a separate // function ensures we won't access RBNodes that may no longer contain what we expect. scanLine.Remove(obstacle.ActiveLowSide, closeVertEvent.Site); scanLine.Remove(obstacle.ActiveHighSide, closeVertEvent.Site); }