// 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(); }
public double GetDistance(Rectangle rect1, Rectangle rect2) { if (!Rectangle.Intersect(rect1, rect2).IsEmpty) return 0; Rectangle leftmost = rect1.Left <= rect2.Left ? rect1 : rect2; Rectangle notLeftmost = rect1.Left <= rect2.Left ? rect2 : rect1; Rectangle botommost = rect1.Bottom <= rect2.Bottom ? rect1 : rect2; Rectangle notBotommost = rect1.Bottom <= rect2.Bottom ? rect2 : rect1; double dx = notLeftmost.Left - leftmost.Right; double dy = notBotommost.Bottom - botommost.Top; if(rect1.IntersectsOnX(rect2)) return dy; if (rect1.IntersectsOnY(rect2)) return dx; return Math.Sqrt(dx * dx + dy * dy); }