HitTestBehavior InsideObstacleHitTest(Point location, Obstacle obstacle) { if ((obstacle == insideHitTestIgnoreObstacle1) || (obstacle == insideHitTestIgnoreObstacle2)) { // It's one of the two obstacles we already know about. return(HitTestBehavior.Continue); } if (obstacle.IsGroup) { // Groups are handled differently from overlaps; we create ScanSegments (overlapped // if within a non-group obstacle, else non-overlapped), and turn on/off access across // the Group boundary vertices. return(HitTestBehavior.Continue); } if (!StaticGraphUtility.PointIsInRectangleInterior(location, obstacle.VisibilityBoundingBox)) { // The point is on the obstacle boundary, not inside it. return(HitTestBehavior.Continue); } // Note: There are rounding issues using Curve.PointRelativeToCurveLocation at angled // obstacle boundaries, hence this function. Point high = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location , insideHitTestScanDirection.Direction) + insideHitTestScanDirection.DirectionAsPoint; Point low = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location , insideHitTestScanDirection.OppositeDirection) - insideHitTestScanDirection.DirectionAsPoint; var testSeg = new LineSegment(low, high); IList <IntersectionInfo> xxs = Curve.GetAllIntersections(testSeg, obstacle.VisibilityPolyline, true /*liftIntersections*/); // If this is an extreme point it can have one intersection, in which case we're either on the border // or outside; if it's a collinear flat boundary, there can be 3 intersections to this point which again // means we're on the border (and 3 shouldn't happen anymore with the curve intersection fixes and // PointIsInsideRectangle check above). So the interesting case is that we have 2 intersections. if (2 == xxs.Count) { Point firstInt = SpliceUtility.RawIntersection(xxs[0], location); Point secondInt = SpliceUtility.RawIntersection(xxs[1], location); // If we're on either intersection, we're on the border rather than inside. if (!PointComparer.Equal(location, firstInt) && !PointComparer.Equal(location, secondInt) && (location.CompareTo(firstInt) != location.CompareTo(secondInt))) { // We're inside. However, this may be an almost-flat side, in which case rounding // could have reported the intersection with the start or end of the same side and // a point somewhere on the interior of that side. Therefore if both intersections // are on the same side (integral portion of the parameter), we consider location // to be on the border. testSeg is always xxs[*].Segment0. Debug.Assert(testSeg == xxs[0].Segment0, "incorrect parameter ordering to GetAllIntersections"); if (!ApproximateComparer.Close(Math.Floor(xxs[0].Par1), Math.Floor(xxs[1].Par1))) { return(HitTestBehavior.Stop); } } } return(HitTestBehavior.Continue); }
internal ObstaclePortEntrance(ObstaclePort oport, Point unpaddedBorderIntersect, Direction outDir, ObstacleTree obstacleTree) { ObstaclePort = oport; UnpaddedBorderIntersect = unpaddedBorderIntersect; OutwardDirection = outDir; // Get the padded intersection. var lineSeg = new LineSegment(UnpaddedBorderIntersect, StaticGraphUtility.RectangleBorderIntersect( oport.Obstacle.VisibilityBoundingBox, UnpaddedBorderIntersect, outDir)); IList <IntersectionInfo> xxs = Curve.GetAllIntersections(lineSeg, oport.Obstacle.VisibilityPolyline, true /*liftIntersections*/); Debug.Assert(1 == xxs.Count, "Expected one intersection"); this.VisibilityBorderIntersect = ApproximateComparer.Round(SpliceUtility.RawIntersection(xxs[0], UnpaddedBorderIntersect)); this.MaxVisibilitySegment = obstacleTree.CreateMaxVisibilitySegment(this.VisibilityBorderIntersect, this.OutwardDirection, out this.pointAndCrossingsList); // Groups are never in a clump (overlapped) but they may still have their port entrance overlapped. if (this.Obstacle.IsOverlapped || (this.Obstacle.IsGroup && !this.Obstacle.IsInConvexHull)) { this.IsOverlapped = obstacleTree.IntersectionIsInsideAnotherObstacle(/*sideObstacle:*/ null, this.Obstacle , this.VisibilityBorderIntersect, ScanDirection.GetInstance(OutwardDirection)); if (!this.Obstacle.IsGroup || this.IsOverlapped || this.InteriorEdgeCrossesObstacle(obstacleTree)) { unpaddedToPaddedBorderWeight = ScanSegment.OverlappedWeight; } } if (this.Obstacle.IsInConvexHull && (unpaddedToPaddedBorderWeight == ScanSegment.NormalWeight)) { SetUnpaddedToPaddedBorderWeightFromHullSiblingOverlaps(obstacleTree); } }
protected override void ProcessVertexEvent(RBNode <BasicObstacleSide> lowSideNode, RBNode <BasicObstacleSide> highSideNode, BasicVertexEvent vertexEvent) { var vertexPoints = (base.ScanDirection.IsHorizontal) ? this.horizontalVertexPoints : this.verticalVertexPoints; vertexPoints.Insert(vertexEvent.Site); // For easier reading... var lowNborSide = LowNeighborSides.LowNeighbor.Item; var highNborSide = HighNeighborSides.HighNeighbor.Item; var highDir = base.ScanDirection.Direction; var lowDir = base.ScanDirection.OppositeDirection; // Generate the neighbor side intersections, regardless of overlaps; these are the type-2 Steiner points. var lowSteiner = ScanLineIntersectSide(vertexEvent.Site, lowNborSide); var highSteiner = ScanLineIntersectSide(vertexEvent.Site, highNborSide); // Add the intersections at the neighbor bounding boxes if the intersection is not at a sentinel. // Go in the opposite direction from the neighbor intersection to find the border between the Steiner // point and vertexEvent.Site (unless vertexEvent.Site is inside the bounding box). if (ObstacleTree.GraphBox.Contains(lowSteiner)) { var bboxIntersectBeforeLowSteiner = StaticGraphUtility.RectangleBorderIntersect(lowNborSide.Obstacle.VisibilityBoundingBox, lowSteiner, highDir); if (PointComparer.IsPureLower(bboxIntersectBeforeLowSteiner, vertexEvent.Site)) { this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeLowSteiner); } } if (ObstacleTree.GraphBox.Contains(highSteiner)) { var bboxIntersectBeforeHighSteiner = StaticGraphUtility.RectangleBorderIntersect(highNborSide.Obstacle.VisibilityBoundingBox, highSteiner, lowDir); if (PointComparer.IsPureLower(vertexEvent.Site, bboxIntersectBeforeHighSteiner)) { this.boundingBoxSteinerPoints.Insert(bboxIntersectBeforeHighSteiner); } } // Add the corners of the bounding box of the vertex obstacle, if they are visible to the event site. // This ensures that we "go around" the obstacle, as with the non-orthogonal edges in the paper. Point lowCorner, highCorner; GetBoundingCorners(lowSideNode.Item.Obstacle.VisibilityBoundingBox, vertexEvent is OpenVertexEvent, this.ScanDirection.IsHorizontal, out lowCorner, out highCorner); if (PointComparer.IsPureLower(lowSteiner, lowCorner) || lowNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle)) { vertexPoints.Insert(lowCorner); } if (PointComparer.IsPureLower(highCorner, highSteiner) || highNborSide.Obstacle.IsInSameClump(vertexEvent.Obstacle)) { vertexPoints.Insert(highCorner); } }
/// <summary> /// Create a LineSegment that contains the max visibility from startPoint in the desired direction. /// </summary> internal LineSegment CreateMaxVisibilitySegment(Point startPoint, Directions dir, out PointAndCrossingsList pacList) { var graphBoxBorderIntersect = StaticGraphUtility.RectangleBorderIntersect(this.GraphBox, startPoint, dir); if (PointComparer.GetDirections(startPoint, graphBoxBorderIntersect) == Directions.None) { pacList = null; return(new LineSegment(startPoint, startPoint)); } var segment = this.RestrictSegmentWithObstacles(startPoint, graphBoxBorderIntersect); // Store this off before other operations which overwrite it. pacList = this.CurrentGroupBoundaryCrossingMap.GetOrderedListBetween(segment.Start, segment.End); return(segment); }
internal List <DebugCurve> Test_GetScanLineDebugCurves() { // ReSharper restore InconsistentNaming var debugCurves = new List <DebugCurve>(); // Alternate the colors between green and blue, so that any inconsistency will stand out. // Use red to highlight that. string[] colors = { "green", "blue" }; int index = 0; var bbox = new Rectangle(); BasicObstacleSide prevSide = null; foreach (var currentSide in SideTree) { string color = colors[index]; index ^= 1; if (null == prevSide) { // Create this the first time through; adding to an empty rectangle leaves 0,0. bbox = new Rectangle(currentSide.Start, currentSide.End); } else { if (-1 != Compare(prevSide, currentSide)) { // Note: we toggled the index, so the red replaces the colour whose turn it is now // and will leave the red line bracketed by two sides of the same colour. color = "red"; } bbox.Add(currentSide.Start); bbox.Add(currentSide.End); } debugCurves.Add(new DebugCurve(0.1, color, new LineSegment(currentSide.Start, currentSide.End))); prevSide = currentSide; } // Add the sweep line. Point start = StaticGraphUtility.RectangleBorderIntersect(bbox, this.linePositionAtLastInsertOrRemove, scanDirection.OppositeDirection); Point end = StaticGraphUtility.RectangleBorderIntersect(bbox, this.linePositionAtLastInsertOrRemove, scanDirection.Direction); debugCurves.Add(new DebugCurve(0.025, "black", new LineSegment(start, end))); return(debugCurves); }