private static bool GetNextSpliceSource(ref VisibilityVertex spliceSource, Direction spliceTargetDir, Direction extendDir) { VisibilityVertex nextSpliceSource = StaticGraphUtility.FindAdjacentVertex(spliceSource, extendDir); if (nextSpliceSource == null) { // 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.FindAdjacentVertex(nextSpliceSource, CompassVector.OppositeDir(spliceTargetDir)); if (nextSpliceSource == null) { return(false); } var nextSpliceSourceExtend = StaticGraphUtility.FindAdjacentVertex(nextSpliceSource, extendDir); if (nextSpliceSourceExtend != null) { nextSpliceSource = nextSpliceSourceExtend; break; } } } spliceSource = nextSpliceSource; return(true); }
private static VisibilityVertex GetSpliceTarget(ref VisibilityVertex spliceSource, Direction 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. Direction prevDir = PointComparer.GetPureDirection(spliceSource.Point, nextExtendPoint); Direction nextDir = prevDir; var spliceTarget = spliceSource; while (nextDir == prevDir) { spliceSource = spliceTarget; spliceTarget = StaticGraphUtility.FindAdjacentVertex(spliceSource, spliceTargetDir); if (spliceTarget == null) { 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.FindAdjacentVertex(spliceTarget, spliceTargetDir); break; } nextDir = PointComparer.GetPureDirection(spliceTarget.Point, nextExtendPoint); } return(spliceTarget); }
internal VisibilityEdge FindPerpendicularOrContainingEdge(VisibilityVertex startVertex , Direction 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.FindAdjacentVertex(startVertex, dir); if (nextVertex == null) { break; } Direction 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 static bool FindBracketingVertices(VisibilityVertex sourceVertex, Point targetPoint, Direction 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.FindAdjacentVertex(bracketSource, dirToTarget); if (bracketTarget == null) { 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(bracketTarget != null); }
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, Direction 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. Direction 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. Direction spliceSourceDir = CompassVector.RotateLeft(extendDir); VisibilityVertex spliceSource = StaticGraphUtility.FindAdjacentVertex(startVertex, spliceSourceDir); if (spliceSource == null) { spliceSourceDir = CompassVector.OppositeDir(spliceSourceDir); spliceSource = StaticGraphUtility.FindAdjacentVertex(startVertex, spliceSourceDir); if (spliceSource == null) { return; } } // Store this off before ExtendSpliceWorker, which overwrites it. Direction 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); }
private static VisibilityVertex TraverseToFirstVertexAtOrAbove(VisibilityVertex startVertex, Point start, Direction dir) { var returnVertex = startVertex; var oppositeDir = CompassVector.OppositeDir(dir); for ( ; ;) { var nextVertex = StaticGraphUtility.FindAdjacentVertex(returnVertex, dir); // This returns Directions. None on a match. if ((nextVertex == null) || (PointComparer.GetDirections(nextVertex.Point, start) == oppositeDir)) { break; } returnVertex = nextVertex; } return(returnVertex); }
internal VisibilityEdge FindNearestPerpendicularOrContainingEdge(VisibilityVertex startVertex , Direction 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. Direction dirTowardLocation = ~dir& PointComparer.GetDirections(startVertex.Point, pointLocation); // If Directions. None then pointLocation is collinear. VisibilityVertex currentVertex = startVertex; Direction currentDirTowardLocation = dirTowardLocation; // First move toward pointLocation far as we can. while (Direction.None != currentDirTowardLocation) { VisibilityVertex nextVertex = StaticGraphUtility.FindAdjacentVertex(currentVertex, dirTowardLocation); if (nextVertex == null) { 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 ((perpEdge != null) || (currentVertex == startVertex)) { break; } currentVertex = StaticGraphUtility.FindAdjacentVertex(currentVertex, CompassVector.OppositeDir(dirTowardLocation)); } return(perpEdge); }
// The return value is whether we should try a second pass if this is called on the first pass, // using spliceTarget to wrap up dead-ends on the target side. bool ExtendSpliceWorker(VisibilityVertex spliceSource, Direction extendDir, Direction spliceTargetDir , LineSegment maxDesiredSegment, LineSegment maxVisibilitySegment , bool isOverlapped, out VisibilityVertex spliceTarget) { // This is called after having created at least one extension vertex (initially, the // first one added outside the obstacle), so we know extendVertex will be there. spliceSource // is the vertex to the OppositeDir(spliceTargetDir) of that extendVertex. VisibilityVertex extendVertex = StaticGraphUtility.FindAdjacentVertex(spliceSource, spliceTargetDir); spliceTarget = StaticGraphUtility.FindAdjacentVertex(extendVertex, spliceTargetDir); for (; ;) { if (!GetNextSpliceSource(ref spliceSource, spliceTargetDir, extendDir)) { break; } // spliceSource is now on the correct edge relative to the desired nextExtendPoint. // spliceTarget is in the opposite direction of the extension-line-to-spliceSource. Point nextExtendPoint = StaticGraphUtility.FindBendPointBetween(extendVertex.Point , spliceSource.Point, CompassVector.OppositeDir(spliceTargetDir)); // We test below for being on or past maxDesiredSegment; here we may be skipping // over maxDesiredSegmentEnd which is valid since we want to be sure to go to or // past limitRect, but be sure to stay within maxVisibilitySegment. if (IsPointPastSegmentEnd(maxVisibilitySegment, nextExtendPoint)) { break; } spliceTarget = GetSpliceTarget(ref spliceSource, spliceTargetDir, nextExtendPoint); //StaticGraphUtility.Test_DumpVisibilityGraph(ObstacleTree, VisGraph); if (spliceTarget == null) { // This may be because spliceSource was created just for Group boundaries. If so, // skip to the next nextExtendVertex location. if (this.IsSkippableSpliceSourceWithNullSpliceTarget(spliceSource, extendDir)) { continue; } // We're at a dead-end extending from the source side, or there is an intervening obstacle, or both. // Don't splice across lateral group boundaries. if (ObstacleTree.SegmentCrossesAnObstacle(spliceSource.Point, nextExtendPoint)) { return(false); } } // We might be walking through a point where a previous chain dead-ended. VisibilityVertex nextExtendVertex = VisGraph.FindVertex(nextExtendPoint); if (nextExtendVertex != null) { if ((spliceTarget == null) || (this.VisGraph.FindEdge(extendVertex.Point, nextExtendPoint) != null)) { // We are probably along a ScanSegment so visibility in this direction has already been determined. // Stop and don't try to continue extension from the opposite side. If we continue splicing here // it might go across an obstacle. if (spliceTarget == null) { Debug_VerifyNonOverlappedExtension(isOverlapped, extendVertex, nextExtendVertex, spliceSource: null, spliceTarget: null); FindOrAddEdge(extendVertex, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight); } return(false); } // This should always have been found in the find-the-next-target loop above if there is // a vertex (which would be nextExtendVertex, which we just found) between spliceSource // and spliceTarget. Even for a sparse graph, an edge should not skip over a vertex. StaticGraphUtility.Assert(spliceTarget == StaticGraphUtility.FindAdjacentVertex(nextExtendVertex, spliceTargetDir) , "no edge exists between an existing nextExtendVertex and spliceTarget" , ObstacleTree, VisGraph); } else { StaticGraphUtility.Assert((spliceTarget == null) || spliceTargetDir == PointComparer.GetPureDirection(nextExtendPoint, spliceTarget.Point) , "spliceTarget is not to spliceTargetDir of nextExtendVertex" , ObstacleTree, VisGraph); nextExtendVertex = this.AddVertex(nextExtendPoint); } FindOrAddEdge(extendVertex, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight); Debug_VerifyNonOverlappedExtension(isOverlapped, extendVertex, nextExtendVertex, spliceSource, spliceTarget); // This will split the edge if targetVertex is non-null; otherwise we are at a dead-end // on the target side so must not create a vertex as it would be inside an obstacle. FindOrAddEdge(spliceSource, nextExtendVertex, isOverlapped ? ScanSegment.OverlappedWeight : ScanSegment.NormalWeight); if (isOverlapped) { isOverlapped = this.SeeIfSpliceIsStillOverlapped(extendDir, nextExtendVertex); } extendVertex = nextExtendVertex; // Test GetDirections because it may return Directions. None. if (0 == (extendDir & PointComparer.GetDirections(nextExtendPoint, maxDesiredSegment.End))) { // At or past the desired max extension point, so we're done. spliceTarget = null; break; } } return(spliceTarget != null); }
private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment) { if ((crossingList == null) || (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; currentVertex != null; currentVertex = StaticGraphUtility.FindAdjacentVertex(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; } } }