internal void CreateObstaclePorts(Obstacle obstacle) { // Create ObstaclePorts for all Ports of this obstacle. This just creates the // ObstaclePort object; we don't add its edges/vertices to the graph until we // do the actual routing. foreach (var port in obstacle.Ports) { CreateObstaclePort(obstacle, port); } }
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 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; } }
internal GroupBoundaryCrossing AddIntersection(Point intersection, Obstacle group, Directions dirToInside) { List<GroupBoundaryCrossing> crossings; if (!pointCrossingMap.TryGetValue(intersection, out crossings)) { crossings = new List<GroupBoundaryCrossing>(); pointCrossingMap[intersection] = crossings; } // We may hit the same point on neighbor traversal in multiple directions. We will have more than one item // in this list only if there are multiple group boundaries at this point, which should be unusual. var crossingsCount = crossings.Count; // cache for perf for (int ii = 0; ii < crossingsCount; ++ii) { var crossing = crossings[ii]; if (crossing.Group == group) { // At a given location for a given group, there is only one valid dirToInside. Debug.Assert(dirToInside == crossing.DirectionToInside, "Mismatched dirToInside"); return crossing; } } var newCrossing = new GroupBoundaryCrossing(group, dirToInside); crossings.Add(newCrossing); return newCrossing; }
private ObstaclePort CreateObstaclePort(Obstacle obstacle, Port port) { // This will replace any previous specification for the port (last one wins). Debug.Assert(!obstaclePortMap.ContainsKey(port), "Port is used by more than one obstacle"); if (null == port.Curve) { return null; } var roundedLocation = ApproximateComparer.Round(port.Location); if (PointLocation.Outside == Curve.PointRelativeToCurveLocation(roundedLocation, obstacle.InputShape.BoundaryCurve)) { // Obstacle.Port is outside Obstacle.Shape; handle it as a FreePoint. return null; } if ((obstacle.InputShape.BoundaryCurve != port.Curve) && (PointLocation.Outside == Curve.PointRelativeToCurveLocation(roundedLocation, port.Curve))) { // Obstacle.Port is outside port.Curve; handle it as a FreePoint. return null; } var oport = new ObstaclePort(port, obstacle); obstaclePortMap[port] = oport; return oport; }
internal BasicVertexEvent(Obstacle obstacle, PolylinePoint p) : base(p) { this.Obstacle = obstacle; }
internal static bool ObstaclesIntersect(Obstacle first, Obstacle second) { if (first == second) { return false; } return Curve.CurvesIntersect(first.VisibilityPolyline, second.VisibilityPolyline); }
private void CreatePaddedObstacle(Shape shape) { var obstacle = new Obstacle(shape, this.UseObstacleRectangles, this.Padding); this.ShapeToObstacleMap[shape] = obstacle; this.PortManager.CreateObstaclePorts(obstacle); }
internal CloseVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
internal HighObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir) : base(obstacle, startVertex, scanDir, scanDir.IsVertical /*traverseClockwise*/) { }
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 }
// If true, we have a staircase situation. internal bool IsStaircaseStep(Obstacle reflectionTarget) { return (this.InitialObstacle == reflectionTarget); }
// 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)); }
// 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); }
internal LowObstacleSide(Obstacle obstacle, PolylinePoint startVertex, ScanDirection scanDir) : base(obstacle, startVertex, scanDir, scanDir.IsHorizontal /*traverseClockwise*/) { }
internal BasicVertexEvent(Obstacle obstacle, PolylinePoint p) : base (p) { this.Obstacle = obstacle; }
internal ObstaclePort(Port port, Obstacle obstacle) { this.Port = port; this.Obstacle = obstacle; this.PortEntrances = new List<ObstaclePortEntrance>(); this.Location = ApproximateComparer.Round(this.Port.Location); }
private static bool ObstaclesAreCloseEnoughToBeConsideredTouching(Obstacle a, Obstacle b, bool aIsInsideB, bool bIsInsideA) { // This is only called when the obstacle.VisibilityPolylines don't intersect, thus one is inside the other // or both are outside. If both are outside then either one's LooseVisibilityPolyline may be used. if (!aIsInsideB && !bIsInsideA) { return(Curve.CurvesIntersect(a.LooseVisibilityPolyline, b.VisibilityPolyline)); } // Otherwise see if the inner one is close enough to the outer border to consider them touching. var innerLoosePolyline = aIsInsideB ? a.LooseVisibilityPolyline : b.LooseVisibilityPolyline; var outerPolyline = aIsInsideB ? b.VisibilityPolyline : a.VisibilityPolyline; foreach (Point innerPoint in innerLoosePolyline) { if (Curve.PointRelativeToCurveLocation(innerPoint, outerPolyline) == PointLocation.Outside) { var outerParamPoint = Curve.ClosestPoint(outerPolyline, innerPoint); if (!ApproximateComparer.CloseIntersections(innerPoint, outerParamPoint)) { return(true); } } } return(false); }
internal HighBendVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
// 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); } } }
internal static bool IsFirstObstacleEntirelyWithinSecond(Obstacle first, Obstacle second, bool touchingOk) { return IsFirstPolylineEntirelyWithinSecond(first.VisibilityPolyline, second.VisibilityPolyline, touchingOk); }
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 LowBendVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
internal void Insert(Obstacle obstacle) { groupsAndClumps.Insert(obstacle); }
// Called by StoreLookaheadSite only. internal BasicReflectionEvent(Obstacle initialObstacle, Obstacle reflectingObstacle, Point site) { this.InitialObstacle = initialObstacle; this.ReflectingObstacle = reflectingObstacle; this.site = site; }
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); } }
internal GroupBoundaryCrossing(Obstacle group, Directions dirToInside) { Debug.Assert(CompassVector.IsPureDirection(dirToInside), "Impure direction"); this.Group = group; this.DirectionToInside = dirToInside; }
// 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); }
internal void RemoveObstaclePorts(Obstacle obstacle) { foreach (var port in obstacle.Ports) { // Since we remove the port from the visibility graph after each routing, all we // have to do here is remove it from the dictionary. RemoveObstaclePort(port); } }
void CreateScanSegments(Obstacle obstacle, NeighborSides neighborSides, BasicVertexEvent vertexEvent) { this.CreateScanSegments(obstacle, (HighObstacleSide)neighborSides.LowNeighbor.Item , (null == neighborSides.LowOverlapEnd) ? null : neighborSides.LowOverlapEnd.Item , (null == neighborSides.HighOverlapEnd) ? null : neighborSides.HighOverlapEnd.Item , (LowObstacleSide)neighborSides.HighNeighbor.Item, vertexEvent); }
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); }
private static void GrowGroupAroundLoosePolyline(Obstacle group, Polyline loosePolyline) { var points = group.VisibilityPolyline.Select(p => p).Concat(loosePolyline.Select(p => p)); group.SetConvexHull(new OverlapConvexHull(ConvexHull.CreateConvexHullAsClosedPolyline(points), new[] { group })); }
protected bool ScanLineCrossesObstacle(Point eventSite, Obstacle obstacle) { // An inner or outer neighbor's side is only an overlap start/stop candidate if its obstacle // brackets the open/close event's Perpendicular Scan coord. return (ScanDirection.ComparePerpCoord(eventSite, obstacle.VisibilityBoundingBox.LeftBottom) > 0) && (ScanDirection.ComparePerpCoord(eventSite, obstacle.VisibilityBoundingBox.RightTop) < 0); }
public bool IsInSameClump(Obstacle other) { return(this.IsOverlapped && (this.Clump == other.Clump)); }
// 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()
// Called by LowReflectionEvent or HighReflectionEvent ctors, which are called out of // AddReflectionEvent, which in turn is called by LoadLookaheadIntersections. // In this case we know the eventObstacle and initialObstacle are the same obstacle (the // one that the reflected ray bounced off of, to generate the Left/HighReflectionEvent). internal BasicReflectionEvent(BasicReflectionEvent previousSite, Obstacle reflectingObstacle, Point site) { this.InitialObstacle = previousSite.ReflectingObstacle; this.ReflectingObstacle = reflectingObstacle; this.site = site; this.PreviousSite = previousSite; }
protected abstract bool InsertParallelReflectionSegment(Point start, Point end, Obstacle eventObstacle, BasicObstacleSide lowNborSide, BasicObstacleSide highNborSide, BasicReflectionEvent action);
// 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; }
internal OpenVertexEvent(Obstacle obstacle, PolylinePoint p) : base(obstacle, p) { }
// If true, we have a staircase situation. internal bool IsStaircaseStep(Obstacle reflectionTarget) { return(this.InitialObstacle == reflectionTarget); }
private void AddGroupIntersectionsToRestrictedRay(Obstacle obstacle, IList<IntersectionInfo> intersections) { // We'll let the lines punch through any intersections with groups, but track the location so we can enable/disable crossing. foreach (var intersectionInfo in intersections) { var intersect = SpliceUtility.RawIntersection(intersectionInfo, currentRestrictedRay.Start); // Skip intersections that are past the end of the restricted segment (though there may still be some // there if we shorten it later, but we'll skip them later). var distSquared = (intersect - currentRestrictedRay.Start).LengthSquared; if (distSquared > restrictedRayLengthSquared) { continue; } var dirTowardIntersect = PointComparer.GetPureDirection(currentRestrictedRay.Start, currentRestrictedRay.End); var polyline = (Polyline)intersectionInfo.Segment1; // this is the second arg to GetAllIntersections var dirsOfSide = CompassVector.VectorDirection(polyline.Derivative(intersectionInfo.Par1)); // The derivative is always clockwise, so if the side contains the rightward rotation of the // direction from the ray origin, then we're hitting it from the inside; otherwise from the outside. var dirToInsideOfGroup = dirTowardIntersect; if (0 != (dirsOfSide & CompassVector.RotateRight(dirTowardIntersect))) { dirToInsideOfGroup = CompassVector.OppositeDir(dirToInsideOfGroup); } CurrentGroupBoundaryCrossingMap.AddIntersection(intersect, obstacle, dirToInsideOfGroup); } }