private void RecurseRestrictRayWithObstacles(RectangleNode <Obstacle, Point> rectNode) { // A lineSeg that moves along the boundary of an obstacle is not blocked by it. if (!StaticGraphUtility.RectangleInteriorsIntersect(currentRestrictedRay.BoundingBox, (Rectangle)rectNode.Rectangle)) { return; } Obstacle obstacle = rectNode.UserData; if (null != obstacle) { // Leaf node. Get the interior intersections. Use the full-length original segment for the intersection calculation. IList <IntersectionInfo> intersections = Curve.GetAllIntersections(restrictedIntersectionTestSegment, obstacle.VisibilityPolyline, true /*liftIntersections*/); if (!obstacle.IsGroup || stopAtGroups) { LookForCloserNonGroupIntersectionToRestrictRay(intersections); return; } if (wantGroupCrossings) { AddGroupIntersectionsToRestrictedRay(obstacle, intersections); } Debug.Assert(rectNode.IsLeaf, "RectNode with UserData is not a Leaf"); return; } // Not a leaf; recurse into children. RecurseRestrictRayWithObstacles(rectNode.Left); RecurseRestrictRayWithObstacles(rectNode.Right); }
private bool InteriorEdgeCrossesObstacle(Rectangle rect, Func <Obstacle, Polyline> whichPolylineToUse, IEnumerable <Obstacle> candidates) { LineSegment lineSeg = null; foreach (var blocker in candidates) { var blockerPolyline = whichPolylineToUse(blocker); if (!StaticGraphUtility.RectangleInteriorsIntersect(rect, blockerPolyline.BoundingBox)) { continue; } lineSeg = lineSeg ?? new LineSegment(this.UnpaddedBorderIntersect, this.VisibilityBorderIntersect); var xx = Curve.CurveCurveIntersectionOne(lineSeg, blockerPolyline, liftIntersection: false); if (xx != null) { return(true); } if (PointLocation.Outside != Curve.PointRelativeToCurveLocation(this.UnpaddedBorderIntersect, blockerPolyline)) { return(true); } } return(false); }
private static bool GetNextSpliceSource(ref VisibilityVertex spliceSource, Directions spliceTargetDir, Directions extendDir) { VisibilityVertex nextSpliceSource = StaticGraphUtility.FindNextVertex(spliceSource, extendDir); if (null == nextSpliceSource) { // See if there is a source further away from the extension line - we might have // been on freePoint line (or another nearby PortEntry line) that dead-ended. // Look laterally from the previous spliceSource first. nextSpliceSource = spliceSource; for (;;) { nextSpliceSource = StaticGraphUtility.FindNextVertex(nextSpliceSource, CompassVector.OppositeDir(spliceTargetDir)); if (null == nextSpliceSource) { return(false); } var nextSpliceSourceExtend = StaticGraphUtility.FindNextVertex(nextSpliceSource, extendDir); if (null != nextSpliceSourceExtend) { nextSpliceSource = nextSpliceSourceExtend; break; } } } spliceSource = nextSpliceSource; return(true); }
internal VisibilityEdge FindPerpendicularOrContainingEdge(VisibilityVertex startVertex , Directions dir, Point pointLocation) { // Return the edge in 'dir' from startVertex that is perpendicular to pointLocation. // startVertex must therefore be located such that pointLocation is in 'dir' direction from it, // or is on the same line. StaticGraphUtility.Assert(0 == (CompassVector.OppositeDir(dir) & PointComparer.GetDirections(startVertex.Point, pointLocation)) , "the ray from 'dir' is away from pointLocation", ObstacleTree, VisGraph); while (true) { VisibilityVertex nextVertex = StaticGraphUtility.FindNextVertex(startVertex, dir); if (null == nextVertex) { break; } Directions dirCheck = PointComparer.GetDirections(nextVertex.Point, pointLocation); // If the next vertex is past the intersection with pointLocation, this edge brackets it. if (0 != (CompassVector.OppositeDir(dir) & dirCheck)) { return(VisGraph.FindEdge(startVertex.Point, nextVertex.Point)); } startVertex = nextVertex; } return(null); }
internal void ConnectVertexToTargetVertex(VisibilityVertex sourceVertex, VisibilityVertex targetVertex, Directions finalEdgeDir, double weight) { // finalDir is the required direction of the final edge to the targetIntersect // (there will be two edges if we have to add a bend vertex). StaticGraphUtility.Assert(PointComparer.IsPureDirection(finalEdgeDir), "finalEdgeDir is not pure", ObstacleTree, VisGraph); // targetIntersect may be CenterVertex if that is on an extreme bend or a flat border. if (PointComparer.Equal(sourceVertex.Point, targetVertex.Point)) { return; } // If the target is collinear with sourceVertex we can just create one edge to it. Directions targetDirs = PointComparer.GetDirections(sourceVertex.Point, targetVertex.Point); if (PointComparer.IsPureDirection(targetDirs)) { FindOrAddEdge(sourceVertex, targetVertex); return; } // Not collinear so we need to create a bend vertex and edge if they don't yet exist. Point bendPoint = StaticGraphUtility.FindBendPointBetween(sourceVertex.Point, targetVertex.Point, finalEdgeDir); VisibilityVertex bendVertex = FindOrAddVertex(bendPoint); FindOrAddEdge(sourceVertex, bendVertex, weight); // Now create the outer target vertex if it doesn't exist. FindOrAddEdge(bendVertex, targetVertex, weight); }
static internal Point RawIntersection(IntersectionInfo xx, Point origin) { // If this fires, then you didn't pass the LineSegment as the first argument to GetAllIntersections. Debug.Assert(xx.Segment0 is LineSegment, "LineSegment was not first arg to GetAllIntersections"); // The intersection snaps the end of the intersection to the PolylinePoint at the start/end // of the interesecting segment on the obstacle if the intersection is Curve.CloseIntersections // to that segment endpoint, which can return a point that is just more than Curve.DistanceEpsilon // off the line. Therefore, re-create the intersection using the LineSegment and intersection // parameters (this assumes the LineSegment.End is not Curve.CloseIntersections to the intersection). Point point = xx.Segment0[xx.Par0]; #if TEST_MSAGL // This may legitimately be rounding-error'd in the same way as xx.IntersectionPoint (and the // caller addresses this later). The purpose of the assert is to verify that the LineSegment // interception is not outside the bbox in the perpendicular direction. var lineSeg = (LineSegment)xx.Segment0; if (StaticGraphUtility.IsVertical(PointComparer.GetDirections(lineSeg.Start, lineSeg.End))) { Debug.Assert(PointComparer.Equal(point.X, origin.X), "segment0 obstacle intersection is off the vertical line"); } else { Debug.Assert(PointComparer.Equal(point.Y, origin.Y), "segment0 obstacle intersection is off the horizontal line"); } #endif // TEST_MSAGL return(ApproximateComparer.Round(point)); }
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); } }
private static VisibilityVertex GetSpliceTarget(ref VisibilityVertex spliceSource, Directions spliceTargetDir, Point nextExtendPoint) { // Look for the target. There may be a dead-ended edge starting at the current spliceSource // edge that has a vertex closer to the extension line; in that case keep walking until we // have the closest vertex on the Source side of the extension line as spliceSource. Directions prevDir = PointComparer.GetPureDirection(spliceSource.Point, nextExtendPoint); Directions nextDir = prevDir; var spliceTarget = spliceSource; while (nextDir == prevDir) { spliceSource = spliceTarget; spliceTarget = StaticGraphUtility.FindNextVertex(spliceSource, spliceTargetDir); if (null == spliceTarget) { break; } if (PointComparer.Equal(spliceTarget.Point, nextExtendPoint)) { // If we encountered an existing vertex for the extension chain, update spliceTarget // to be after it and we're done with this loop. spliceTarget = StaticGraphUtility.FindNextVertex(spliceTarget, spliceTargetDir); break; } nextDir = PointComparer.GetPureDirection(spliceTarget.Point, nextExtendPoint); } return(spliceTarget); }
// Append a vertex before LowestVisibilityVertex or after HighestVisibilityVertex. internal void AppendVisibilityVertex(VisibilityGraph vg, VisibilityVertex newVertex) { Debug.Assert(null != newVertex, "newVertex must not be null"); Debug.Assert((null == LowestVisibilityVertex) == (null == HighestVisibilityVertex), "Mismatched null Lowest/HighestVisibilityVertex"); Debug.Assert(StaticGraphUtility.PointIsOnSegment(this, newVertex.Point), "newVertex is out of segment range"); if (null == HighestVisibilityVertex) { if (!AddGroupCrossingsBeforeHighestVisibilityVertex(vg, newVertex)) { SetInitialVisibilityVertex(newVertex); } } else { // In the event of overlaps where ScanSegments share a Start/End at a border, SegmentIntersector // may be appending the same Vertex twice. If that point is on the border of a group, // then we may have just added the border-crossing edge as well. if (PointComparer.IsPureLower(newVertex.Point, HighestVisibilityVertex.Point)) { Debug.Assert(null != vg.FindEdge(newVertex.Point, HighestVisibilityVertex.Point) , "unexpected low/middle insertion to ScanSegment"); return; } // Add the new edge. This will always be in the ascending direction. if (!AddGroupCrossingsBeforeHighestVisibilityVertex(vg, newVertex)) { AppendHighestVisibilityVertex(newVertex); } } }
internal static bool FindBracketingVertices(VisibilityVertex sourceVertex, Point targetPoint, Directions dirToTarget , out VisibilityVertex bracketSource, out VisibilityVertex bracketTarget) { // Walk from the source to target until we bracket target or there is no nextVertex // in the desired direction. bracketSource = sourceVertex; for (; ;) { bracketTarget = StaticGraphUtility.FindNextVertex(bracketSource, dirToTarget); if (null == bracketTarget) { break; } if (PointComparer.Equal(bracketTarget.Point, targetPoint)) { // Desired edge already exists. return(true); } if (dirToTarget != PointComparer.GetPureDirection(bracketTarget.Point, targetPoint)) { // bracketTarget is past vertex in the traversal direction. break; } bracketSource = bracketTarget; } return(null != bracketTarget); }
// ctor internal void Update(Point start, Point end) { Debug.Assert(PointComparer.Equal(start, end) || StaticGraphUtility.IsAscending(PointComparer.GetPureDirection(start, end)) , "non-ascending segment"); startPoint = start; endPoint = end; }
private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment) { if ((null == crossingList) || (0 == crossingList.Count)) { return; } crossingList.Reset(); var start = maxSegment.Start; var end = maxSegment.End; var dir = PointComparer.GetPureDirection(start, end); // Make sure we are going in the ascending direction. if (!StaticGraphUtility.IsAscending(dir)) { start = maxSegment.End; end = maxSegment.Start; dir = CompassVector.OppositeDir(dir); } // We need to back up to handle group crossings that are between a VisibilityBorderIntersect on a sloped border and the // incoming startVertex (which is on the first ScanSegment in Perpendicular(dir) that is outside that padded border). startVertex = TraverseToFirstVertexAtOrAbove(startVertex, start, CompassVector.OppositeDir(dir)); // Splice into the Vertices between and including the start/end points. for (var currentVertex = startVertex; null != currentVertex; currentVertex = StaticGraphUtility.FindNextVertex(currentVertex, dir)) { bool isFinalVertex = (PointComparer.Compare(currentVertex.Point, end) >= 0); while (crossingList.CurrentIsBeforeOrAt(currentVertex.Point)) { PointAndCrossings pac = crossingList.Pop(); // If it's past the start and at or before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) > 0) { if (PointComparer.Compare(pac.Location, end) <= 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, CompassVector.OppositeDir(dir)); } } // If it's at or past the start and before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) >= 0) { if (PointComparer.Compare(pac.Location, end) < 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, dir); } } } if (isFinalVertex) { break; } } }
// Use the internal static xxxInstance properties to get an instance. private ScanDirection(Directions directionAlongScanLine) { System.Diagnostics.Debug.Assert(StaticGraphUtility.IsAscending(directionAlongScanLine), "directionAlongScanLine must be ascending"); Direction = directionAlongScanLine; DirectionAsPoint = CompassVector.ToPoint(Direction); PerpDirection = (Directions.North == directionAlongScanLine) ? Directions.East : Directions.North; PerpDirectionAsPoint = CompassVector.ToPoint(PerpDirection); OppositeDirection = CompassVector.OppositeDir(directionAlongScanLine); }
internal bool HasGroupCrossingBeforePoint(Point point) { if (!this.HasGroupCrossings) { return(false); } var pac = StaticGraphUtility.IsAscending(this.OutwardDirection) ? this.pointAndCrossingsList.First : this.pointAndCrossingsList.Last; return(PointComparer.GetDirections(this.MaxVisibilitySegment.Start, pac.Location) == PointComparer.GetDirections(pac.Location, point)); }
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; } }
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); } }
bool IsSkippableSpliceSourceWithNullSpliceTarget(VisibilityVertex spliceSource, Directions extendDir) { if (IsSkippableSpliceSourceEdgeWithNullTarget(StaticGraphUtility.FindNextEdge(VisGraph, spliceSource, extendDir))) { return(true); } var spliceSourceEdge = StaticGraphUtility.FindNextEdge(this.VisGraph, spliceSource, CompassVector.OppositeDir(extendDir)); // Since target is null, if this is a reflection, it is bouncing off an outer side of a group or // obstacle at spliceSource. In that case, we don't want to splice from it because then we could // cut through the group and outside again; instead we should just stay outside it. return(IsSkippableSpliceSourceEdgeWithNullTarget(spliceSourceEdge) || IsReflectionEdge(spliceSourceEdge)); }
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; }
/// <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); }
private VisibilityEdge AddVisibilityEdge(VisibilityVertex source, VisibilityVertex target) { Debug.Assert(source.Point != target.Point, "Self-edges are not allowed"); Debug.Assert(PointComparer.IsPureLower(source.Point, target.Point), "Impure or reversed direction encountered"); // Make sure we aren't adding two edges in the same direction to the same vertex. Debug.Assert(null == StaticGraphUtility.FindAdjacentVertex(source, StaticGraphUtility.EdgeDirection(source, target)) , "Duplicate outEdge from Source vertex"); Debug.Assert(null == StaticGraphUtility.FindAdjacentVertex(target, StaticGraphUtility.EdgeDirection(target, source)) , "Duplicate inEdge to Target vertex"); var edge = new VisibilityEdge(source, target, this.Weight); VisibilityGraph.AddEdge(edge); return(edge); }
private void ExtendEdgeChain(VisibilityVertex startVertex, Directions extendDir , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment , PointAndCrossingsList pacList, bool isOverlapped) { StaticGraphUtility.Assert(PointComparer.GetPureDirection(maxDesiredSegment.Start, maxDesiredSegment.End) == extendDir , "maxDesiredSegment is reversed", ObstacleTree, VisGraph); // Direction*s*, because it may return None, which is valid and means startVertex is on the // border of an obstacle and we don't want to go inside it. Directions segmentDir = PointComparer.GetDirections(startVertex.Point, maxDesiredSegment.End); if (segmentDir != extendDir) { // OppositeDir may happen on overlaps where the boundary has a gap in its ScanSegments due to other obstacles // overlapping it and each other. This works because the port has an edge connected to startVertex, // which is on a ScanSegment outside the obstacle. StaticGraphUtility.Assert(isOverlapped || (segmentDir != CompassVector.OppositeDir(extendDir)) , "obstacle encountered between prevPoint and startVertex", ObstacleTree, VisGraph); return; } // We'll find the segment to the left (or right if to the left doesn't exist), // then splice across in the opposite direction. Directions spliceSourceDir = CompassVector.RotateLeft(extendDir); VisibilityVertex spliceSource = StaticGraphUtility.FindNextVertex(startVertex, spliceSourceDir); if (null == spliceSource) { spliceSourceDir = CompassVector.OppositeDir(spliceSourceDir); spliceSource = StaticGraphUtility.FindNextVertex(startVertex, spliceSourceDir); if (null == spliceSource) { return; } } // Store this off before ExtendSpliceWorker, which overwrites it. Directions spliceTargetDir = CompassVector.OppositeDir(spliceSourceDir); VisibilityVertex spliceTarget; if (ExtendSpliceWorker(spliceSource, extendDir, spliceTargetDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget)) { // We ended on the source side and may have dead-ends on the target side so reverse sides. ExtendSpliceWorker(spliceTarget, extendDir, spliceSourceDir, maxDesiredSegment, maxVisibilitySegment, isOverlapped, out spliceTarget); } SpliceGroupBoundaryCrossings(pacList, startVertex, maxDesiredSegment); }
internal VisibilityEdge SplitEdge(VisibilityEdge edge, VisibilityVertex splitVertex) { // If the edge is NULL it means we could not find an appropriate one, so do nothing. if (null == edge) { return(null); } StaticGraphUtility.Assert(StaticGraphUtility.PointIsOnSegment(edge.SourcePoint, edge.TargetPoint, splitVertex.Point) , "splitVertex is not on edge", ObstacleTree, VisGraph); if (PointComparer.Equal(edge.Source.Point, splitVertex.Point) || PointComparer.Equal(edge.Target.Point, splitVertex.Point)) { // No split needed. return(edge); } // Store the original edge, if needed. if (!(edge is TollFreeVisibilityEdge)) { edgesToRestore.Add(edge); } VisibilityGraph.RemoveEdge(edge); // If this is an overlapped edge, or we're in sparseVg, then it may be an unpadded->padded edge that crosses // over another obstacle's padded boundary, and then either a collinear splice from a free point or another // obstacle in the same cluster starts splicing from that leapfrogged boundary, so we have the edges: // A -> D | D is unpadded, A is padded border of sourceObstacle // B -> C -> E -> F | B and C are vertical ScanSegments between A and D // <-- splice direction is West | F is unpadded, E is padded border of targetObstacle // Now after splicing F to E to C to B we go A, calling FindOrAddEdge B->A; the bracketing process finds // A->D which we'll be splitting at B, which would wind up with A->B, B->C, B->D, having to Eastward // outEdges from B. See RectilinearTests.Reflection_Block1_Big_UseRect for overlapped, and // RectilinearTests.FreePortLocationRelativeToTransientVisibilityEdgesSparseVg for sparseVg. // To avoid this we add the edges in each direction from splitVertex with FindOrAddEdge. If we've // come here from a previous call to FindOrAddEdge, then that call has found the bracketing vertices, // which are the endpoints of 'edge', and we've removed 'edge', so we will not call SplitEdge again. if ((this.IsSparseVg || (edge.Weight == ScanSegment.OverlappedWeight)) && (splitVertex.Degree > 0)) { FindOrAddEdge(splitVertex, edge.Source, edge.Weight); return(FindOrAddEdge(splitVertex, edge.Target, edge.Weight)); } // Splice it into the graph in place of targetEdge. Return the first half, because // this may be called from AddEdge, in which case the split vertex is the target vertex. CreateEdge(splitVertex, edge.Target, edge.Weight); return(CreateEdge(edge.Source, splitVertex, edge.Weight)); }
// Adds an edge from this.Vertex to a (possibly new) vertex at an intersection with an // existing Edge that adjoins the point. We take 'dir' as an input parameter for edge // extension because we may be on the edge so can't calculate the direction. internal VisibilityVertex AddEdgeToAdjacentEdge(TransientGraphUtility transUtil , VisibilityEdge targetEdge, Directions dirToExtend, Rectangle limitRect) { Point targetIntersect = StaticGraphUtility.SegmentIntersection(targetEdge, this.Point); VisibilityVertex targetVertex = transUtil.VisGraph.FindVertex(targetIntersect); if (null != targetVertex) { AddToAdjacentVertex(transUtil, targetVertex, dirToExtend, limitRect); } else { targetVertex = transUtil.AddEdgeToTargetEdge(this.Vertex, targetEdge, targetIntersect); } ExtendEdgeChain(transUtil, targetVertex, dirToExtend, limitRect); return(targetVertex); }
internal VisibilityEdge FindNearestPerpendicularOrContainingEdge(VisibilityVertex startVertex , Directions dir, Point pointLocation) { // Similar to FindPerpendicularEdge, but first try to move closer to pointLocation, // as long as there are edges going in 'dir' that extend to pointLocation. Directions dirTowardLocation = ~dir& PointComparer.GetDirections(startVertex.Point, pointLocation); // If Directions. None then pointLocation is collinear. VisibilityVertex currentVertex = startVertex; Directions currentDirTowardLocation = dirTowardLocation; // First move toward pointLocation far as we can. while (Directions.None != currentDirTowardLocation) { VisibilityVertex nextVertex = StaticGraphUtility.FindNextVertex(currentVertex, dirTowardLocation); if (null == nextVertex) { break; } if (0 != (CompassVector.OppositeDir(dirTowardLocation) & PointComparer.GetDirections(nextVertex.Point, pointLocation))) { break; } currentVertex = nextVertex; currentDirTowardLocation = ~dir& PointComparer.GetDirections(currentVertex.Point, pointLocation); } // Now find the first vertex that has a chain that intersects pointLocation, if any, moving away // from pointLocation until we find it or arrive back at startVertex. VisibilityEdge perpEdge; while (true) { perpEdge = FindPerpendicularOrContainingEdge(currentVertex, dir, pointLocation); if ((null != perpEdge) || (currentVertex == startVertex)) { break; } currentVertex = StaticGraphUtility.FindNextVertex(currentVertex, CompassVector.OppositeDir(dirTowardLocation)); } return(perpEdge); }
private static VisibilityVertex TraverseToFirstVertexAtOrAbove(VisibilityVertex startVertex, Point start, Directions dir) { var returnVertex = startVertex; var oppositeDir = CompassVector.OppositeDir(dir); for ( ; ;) { var nextVertex = StaticGraphUtility.FindNextVertex(returnVertex, dir); // This returns Directions. None on a match. if ((null == nextVertex) || (PointComparer.GetDirections(nextVertex.Point, start) == oppositeDir)) { break; } returnVertex = nextVertex; } return(returnVertex); }
internal void ExtendEdgeChain(VisibilityVertex startVertex, Rectangle limitRect, LineSegment maxVisibilitySegment, PointAndCrossingsList pacList, bool isOverlapped) { var dir = PointComparer.GetDirections(maxVisibilitySegment.Start, maxVisibilitySegment.End); if (dir == Directions.None) { return; } Debug.Assert(CompassVector.IsPureDirection(dir), "impure max visibility segment"); // Shoot the edge chain out to the shorter of max visibility or intersection with the limitrect. StaticGraphUtility.Assert(PointComparer.Equal(maxVisibilitySegment.Start, startVertex.Point) || (PointComparer.GetPureDirection(maxVisibilitySegment.Start, startVertex.Point) == dir) , "Inconsistent direction found", ObstacleTree, VisGraph); double oppositeFarBound = StaticGraphUtility.GetRectangleBound(limitRect, dir); Point maxDesiredSplicePoint = StaticGraphUtility.IsVertical(dir) ? ApproximateComparer.Round(new Point(startVertex.Point.X, oppositeFarBound)) : ApproximateComparer.Round(new Point(oppositeFarBound, startVertex.Point.Y)); if (PointComparer.Equal(maxDesiredSplicePoint, startVertex.Point)) { // Nothing to do. return; } if (PointComparer.GetPureDirection(startVertex.Point, maxDesiredSplicePoint) != dir) { // It's in the opposite direction, so no need to do anything. return; } // If maxDesiredSplicePoint is shorter, create a new shorter segment. We have to pass both segments // through to the worker function so it knows whether it can go past maxDesiredSegment (which may be limited // by limitRect). var maxDesiredSegment = maxVisibilitySegment; if (PointComparer.GetDirections(maxDesiredSplicePoint, maxDesiredSegment.End) == dir) { maxDesiredSegment = new LineSegment(maxDesiredSegment.Start, maxDesiredSplicePoint); } ExtendEdgeChain(startVertex, dir, maxDesiredSegment, maxVisibilitySegment, pacList, isOverlapped); }
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); }
internal VisibilityVertex AddEdgeToTargetEdge(VisibilityVertex sourceVertex, VisibilityEdge targetEdge , Point targetIntersect) { StaticGraphUtility.Assert(PointComparer.Equal(sourceVertex.Point, targetIntersect) || PointComparer.IsPureDirection(sourceVertex.Point, targetIntersect) , "non-orthogonal edge request", ObstacleTree, VisGraph); StaticGraphUtility.Assert(StaticGraphUtility.PointIsOnSegment(targetEdge.SourcePoint, targetEdge.TargetPoint, targetIntersect) , "targetIntersect is not on targetEdge", ObstacleTree, VisGraph); // If the target vertex does not exist, we must split targetEdge to add it. VisibilityVertex targetVertex = VisGraph.FindVertex(targetIntersect); if (null == targetVertex) { targetVertex = AddVertex(targetIntersect); SplitEdge(targetEdge, targetVertex); } FindOrAddEdge(sourceVertex, targetVertex); return(targetVertex); }
private void Debug_VerifyNonOverlappedExtension(bool isOverlapped, VisibilityVertex extendVertex, VisibilityVertex nextExtendVertex, VisibilityVertex spliceSource, VisibilityVertex spliceTarget) { if (isOverlapped) { return; } #if TEST_MSAGL StaticGraphUtility.Assert(!this.ObstacleTree.SegmentCrossesANonGroupObstacle(extendVertex.Point, nextExtendVertex.Point) , "extendDir edge crosses an obstacle", this.ObstacleTree, this.VisGraph); #endif // TEST_MSAGL if (spliceSource == null) { // Only verifying the direct extension. return; } // Verify lateral splices as well. if ((null == spliceTarget) || (null == this.VisGraph.FindEdge(spliceSource.Point, spliceTarget.Point) && (null == this.VisGraph.FindEdge(spliceSource.Point, nextExtendVertex.Point)))) { // If targetVertex isn't null and the proposed edge from nextExtendVertex -> targetVertex // edge doesn't already exist, then we assert that we're not creating a new edge that // crosses the obstacle bounds (a bounds-crossing edge may already exist, from a port // within the obstacle; in that case nextExtendPoint splits that edge). As above, don't // splice laterally across groups. StaticGraphUtility.Assert(!this.ObstacleTree.SegmentCrossesAnObstacle(spliceSource.Point, nextExtendVertex.Point) , "spliceSource->extendVertex edge crosses an obstacle", this.ObstacleTree, this.VisGraph); // Above we moved spliceTarget over when nextExtendVertex existed, so account // for that here. StaticGraphUtility.Assert((null == spliceTarget) || (null != this.VisGraph.FindEdge(nextExtendVertex.Point, spliceTarget.Point)) || !this.ObstacleTree.SegmentCrossesAnObstacle(nextExtendVertex.Point, spliceTarget.Point) , "extendVertex->spliceTarget edge crosses an obstacle", this.ObstacleTree, this.VisGraph); } }