2020-04-14 00:42:04 +01:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using Microsoft.Msagl.Core ;
using Microsoft.Msagl.Core.DataStructures ;
using Microsoft.Msagl.Core.Geometry ;
using Microsoft.Msagl.Core.Geometry.Curves ;
using Microsoft.Msagl.Core.Layout ;
using Microsoft.Msagl.Layout.Initial ;
using Microsoft.Msagl.Layout.LargeGraphLayout ;
using Microsoft.Msagl.Routing.Spline.ConeSpanner ;
using Microsoft.Msagl.Routing.Visibility ;
#if TEST_MSAGL
using Microsoft.Msagl.DebugHelpers ;
#endif
namespace Microsoft.Msagl.Routing {
/// <summary>
/// the router between nodes
/// </summary>
public class InteractiveEdgeRouter : AlgorithmBase {
/// <summary>
/// the obstacles for routing
/// </summary>
public IEnumerable < ICurve > Obstacles { get ; private set ; }
/// <summary>
/// the minimum angle between a node boundary curve and and an edge
/// curve at the place where the edge curve intersects the node boundary
/// </summary>
double EnteringAngleBound { get ; set ; }
Polyline _sourceTightPolyline ;
Polyline SourceTightPolyline {
get { return _sourceTightPolyline ; }
set { _sourceTightPolyline = value ; }
}
/// <summary>
///
/// </summary>
Polyline SourceLoosePolyline { get ; set ; }
Polyline targetTightPolyline ;
/// <summary>
///
/// </summary>
Polyline TargetTightPolyline {
get { return targetTightPolyline ; }
set { targetTightPolyline = value ; }
}
Polyline targetLoosePolyline ;
Polyline TargetLoosePolyline {
get { return targetLoosePolyline ; }
set { targetLoosePolyline = value ; }
}
//RectangleNode<Polyline> RootOfTightHierarchy {
// get { return this.obstacleCalculator.RootOfTightHierararchy; }
//}
Rectangle activeRectangle = Rectangle . CreateAnEmptyBox ( ) ;
VisibilityGraph visibilityGraph ;
internal VisibilityGraph VisibilityGraph {
get { return visibilityGraph ; }
set { visibilityGraph = value ; }
}
//List<Polyline> activeTightPolylines = new List<Polyline>();
List < Polygon > activePolygons = new List < Polygon > ( ) ;
readonly Set < Polyline > alreadyAddedOrExcludedPolylines = new Set < Polyline > ( ) ;
// Dictionary<Point, Polyline> pointsToObstacles = new Dicitonary<Point, Polyline>();
Port sourcePort ;
/// <summary>
/// the port of the edge start
/// </summary>
public Port SourcePort {
get { return sourcePort ; }
private set {
sourcePort = value ;
if ( sourcePort ! = null ) {
SourceTightPolyline = GetFirstHitPolyline ( sourcePort . Location ,
ObstacleCalculator . RootOfTightHierarchy ) ;
if ( sourcePort is FloatingPort ) {
alreadyAddedOrExcludedPolylines . Insert ( SourceLoosePolyline ) ;
//we need to exclude the loose polyline around the source port from the tangent visibily graph
StartPointOfEdgeRouting = SourcePort . Location ;
}
else {
var bp = ( CurvePort ) sourcePort ;
StartPointOfEdgeRouting = TakeBoundaryPortOutsideOfItsLoosePolyline ( bp . Curve , bp . Parameter ,
SourceLoosePolyline ) ;
}
}
}
}
Port targetPort ;
/// <summary>
/// the port of the edge end
/// </summary>
Port TargetPort {
get { return targetPort ; }
set { targetPort = value ; }
}
/// <summary>
/// the curve should not come closer than Padding to the nodes
/// </summary>
public double TightPadding { get ; set ; }
double loosePadding ;
/// <summary>
/// we further pad each node but not more than LoosePadding.
/// </summary>
public double LoosePadding {
get {
return loosePadding ;
}
internal set {
loosePadding = value ;
if ( ObstacleCalculator ! = null )
ObstacleCalculator . LoosePadding = value ;
}
}
VisibilityVertex _sourceVisibilityVertex ;
VisibilityVertex SourceVisibilityVertex {
get { return _sourceVisibilityVertex ; } // set { sourceVisibilityVertex = value; }
}
VisibilityVertex targetVisibilityVertex ;
VisibilityVertex TargetVisibilityVertex {
get { return targetVisibilityVertex ; } // set { targetVisibilityVertex = value; }
}
Polyline _polyline ;
/// <summary>
///
/// </summary>
double OffsetForPolylineRelaxing { get ; set ; }
/// <summary>
/// Set up the router and calculate the set of obstacles over which to route.
/// </summary>
/// <param name="obstacles">the obstacles for routing</param>
/// <param name="padding">obstacles are inflated by this much to find an inner boundary within which edges cannot enter</param>
/// <param name="loosePadding">
/// obstacles are inflated again by this much to find initial
/// routing but then spline smoothing is allowed to come inside this outer boundary.
/// Loose padding of 0 will give sharp corners (no spline smoothing)</param>
/// <param name="coneSpannerAngle">if this is greater than 0 then a "cone spanner" visibility graph with be
/// generated using cones of the specified angle to search for visibility edges. The cone spanner graph is
/// a sparser graph than the complete visibility graph and is hence much faster to generate and route over
/// but may not give strictly shortest path routes</param>
public InteractiveEdgeRouter ( IEnumerable < ICurve > obstacles , double padding , double loosePadding ,
double coneSpannerAngle ) : this ( obstacles , padding , loosePadding , coneSpannerAngle , false ) { }
/// <summary>
/// The expected number of progress steps this algorithm will take.
/// </summary>
public int ExpectedProgressSteps { get ; private set ; }
bool targetIsInsideOfSourceTightPolyline ;
bool sourceIsInsideOfTargetTightPolyline ;
internal bool UseEdgeLengthMultiplier ;
/// <summary>
/// if set to true the algorithm will try to shortcut a shortest polyline inner points
/// </summary>
internal bool UseInnerPolylingShortcutting = true ;
/// <summary>
/// if set to true the algorithm will try to shortcut a shortest polyline start and end
/// </summary>
internal bool UsePolylineEndShortcutting = true ;
internal bool AllowedShootingStraightLines = true ;
Dictionary < Corner , Tuple < double , double > > cornerTable ;
bool cacheCorners ;
/// <summary>
/// An empty constructor for calling it from inside of MSAGL
/// </summary>
internal InteractiveEdgeRouter ( ) {
ObstacleCalculator = new InteractiveObstacleCalculator ( Obstacles , TightPadding , LoosePadding , false ) ;
}
Point StartPointOfEdgeRouting { get ; set ; }
void ExtendVisibilityGraphToLocation ( Point location ) {
if ( VisibilityGraph = = null )
VisibilityGraph = new VisibilityGraph ( ) ;
List < Polygon > addedPolygons = null ;
if ( ! activeRectangle . Contains ( location ) ) {
if ( activeRectangle . IsEmpty )
activeRectangle = new Rectangle ( SourcePort . Location , location ) ;
else
activeRectangle . Add ( location ) ;
addedPolygons = GetAddedPolygonesAndMaybeExtendActiveRectangle ( ) ;
foreach ( Polygon polygon in addedPolygons )
VisibilityGraph . AddHole ( polygon . Polyline ) ;
}
if ( addedPolygons = = null | | addedPolygons . Count = = 0 ) {
if ( targetVisibilityVertex ! = null )
VisibilityGraph . RemoveVertex ( targetVisibilityVertex ) ;
CalculateEdgeTargetVisibilityGraph ( location ) ;
}
else {
RemovePointVisibilityGraphs ( ) ;
var visibilityGraphGenerator = new InteractiveTangentVisibilityGraphCalculator ( addedPolygons ,
activePolygons ,
VisibilityGraph ) ;
visibilityGraphGenerator . Run ( ) ;
activePolygons . AddRange ( addedPolygons ) ;
CalculateEdgeTargetVisibilityGraph ( location ) ;
CalculateSourcePortVisibilityGraph ( ) ;
}
}
void RemovePointVisibilityGraphs ( ) {
if ( targetVisibilityVertex ! = null )
VisibilityGraph . RemoveVertex ( targetVisibilityVertex ) ;
if ( _sourceVisibilityVertex ! = null )
VisibilityGraph . RemoveVertex ( _sourceVisibilityVertex ) ;
}
void CalculateEdgeTargetVisibilityGraph ( Point location ) {
PointVisibilityCalculator . CalculatePointVisibilityGraph ( GetActivePolylines ( ) , VisibilityGraph , location ,
VisibilityKind . Tangent , out targetVisibilityVertex ) ;
}
void CalculateSourcePortVisibilityGraph ( ) {
PointVisibilityCalculator . CalculatePointVisibilityGraph ( GetActivePolylines ( ) , VisibilityGraph ,
StartPointOfEdgeRouting , VisibilityKind . Tangent ,
out _sourceVisibilityVertex ) ;
Debug . Assert ( _sourceVisibilityVertex ! = null ) ;
}
Point TakeBoundaryPortOutsideOfItsLoosePolyline ( ICurve nodeBoundary , double parameter , Polyline loosePolyline ) {
Point location = nodeBoundary [ parameter ] ;
Point tangent =
( nodeBoundary . LeftDerivative ( parameter ) . Normalize ( ) +
nodeBoundary . RightDerivative ( parameter ) . Normalize ( ) ) . Normalize ( ) ;
if ( Point . GetTriangleOrientation ( PointInsideOfConvexCurve ( nodeBoundary ) , location , location + tangent ) = =
TriangleOrientation . Counterclockwise )
tangent = - tangent ;
tangent = tangent . Rotate ( Math . PI / 2 ) ;
double len = loosePolyline . BoundingBox . Diagonal ;
var ls = new LineSegment ( location , location + len * tangent ) ;
Point p = Curve . GetAllIntersections ( ls , loosePolyline , false ) [ 0 ] . IntersectionPoint ;
Point del = tangent * ( p - location ) . Length * 0.5 ;
//Point del = tangent * this.OffsetForPolylineRelaxing * 2;
while ( true ) {
ls = new LineSegment ( location , p + del ) ;
bool foundIntersectionsOutsideOfSource = false ;
foreach ( IntersectionInfo ii in
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , ObstacleCalculator . RootOfLooseHierarchy ) )
if ( ii . Segment1 ! = loosePolyline ) {
del / = 1.5 ;
foundIntersectionsOutsideOfSource = true ;
break ;
}
if ( ! foundIntersectionsOutsideOfSource )
break ;
}
return ls . End ;
}
static Point PointInsideOfConvexCurve ( ICurve nodeBoundary ) {
return ( nodeBoundary [ 0 ] + nodeBoundary [ 1.5 ] ) / 2 ; //a hack !!!!!!!!!!!!!!!!!!!!!!
}
//Point TakeSourcePortOutsideOfLoosePolyline() {
// CurvePort bp = SourcePort as CurvePort;
// ICurve nodeBoundary = bp.Node.BoundaryCurve;
// Point location = bp.Location;
// Point tangent = (nodeBoundary.LeftDerivative(bp.Parameter).Normalize() + nodeBoundary.RightDerivative(bp.Parameter).Normalize()).Normalize();
// if (Point.GetTriangleOrientation(bp.Node.Center, location, location + tangent) == TriangleOrientation.Counterclockwise)
// tangent = -tangent;
// tangent = tangent.Rotate(Math.PI / 2);
// double len = this.sourceLoosePolyline.BoundingBox.Diagonal;
// Point portLocation = bp.Location;
// LineSegment ls = new LineSegment(portLocation, portLocation + len * tangent);
// Point p = Curve.GetAllIntersections(ls, this.SourceLoosePolyline, false)[0].IntersectionPoint;
// Point del = tangent * this.OffsetForPolylineRelaxing * 2;
// while (true) {
// ls = new LineSegment(portLocation, p + del);
// bool foundIntersectionsOutsideOfSource = false;
// foreach (IntersectionInfo ii in IntersectionsOfLineAndRectangleNodeOverPolyline(ls, this.obstacleCalculator.RootOfLooseHierarchy))
// if (ii.Segment1 != this.SourceLoosePolyline) {
// del /= 1.5;
// foundIntersectionsOutsideOfSource = true;
// break;
// }
// if (!foundIntersectionsOutsideOfSource)
// break;
// }
// return ls.End;
//}
IEnumerable < Polyline > GetActivePolylines ( ) {
foreach ( Polygon polygon in activePolygons )
yield return polygon . Polyline ;
}
List < Polygon > GetAddedPolygonesAndMaybeExtendActiveRectangle ( ) {
Rectangle rect = activeRectangle ;
var addedPolygones = new List < Polygon > ( ) ;
bool added ;
do {
added = false ;
foreach ( Polyline loosePoly in
ObstacleCalculator . RootOfLooseHierarchy . GetNodeItemsIntersectingRectangle ( activeRectangle ) ) {
if ( ! alreadyAddedOrExcludedPolylines . Contains ( loosePoly ) ) {
rect . Add ( loosePoly . BoundingBox ) ;
addedPolygones . Add ( new Polygon ( loosePoly ) ) ;
alreadyAddedOrExcludedPolylines . Insert ( loosePoly ) ;
//we register the loose polyline in the set to not add it twice
added = true ;
}
}
if ( added )
activeRectangle = rect ;
} while ( added ) ;
return addedPolygones ;
}
#region commented out code
// List<Polyline> GetActivePolylines(Rectangle rectangleOfVisibilityGraph) {
// return this.activeTightPolylines;
//}
//bool LineIntersectsTightObstacles(Point point, Point x) {
// return LineIntersectsTightObstacles(new LineSegment(point, x));
//}
#endregion
#if TEST_MSAGL
// void ShowPolylineAndObstaclesWithGraph() {
// List<ICurve> ls = new List<ICurve>();
// foreach (Polyline poly in this.obstacleCalculator.TightObstacles)
// ls.Add(poly);
// foreach (Polyline poly in this.obstacleCalculator.LooseObstacles)
// ls.Add(poly);
// AddVisibilityGraph(ls);
// ls.Add(Polyline);
// SugiyamaLayoutSettings.Show(ls.ToArray());
//}
#endif
//pull the polyline out from the corners
void RelaxPolyline ( ) {
RelaxedPolylinePoint relaxedPolylinePoint = CreateRelaxedPolylinePoints ( _polyline ) ;
for ( relaxedPolylinePoint = relaxedPolylinePoint . Next ;
relaxedPolylinePoint . Next ! = null ;
relaxedPolylinePoint = relaxedPolylinePoint . Next )
RelaxPolylinePoint ( relaxedPolylinePoint ) ;
}
static RelaxedPolylinePoint CreateRelaxedPolylinePoints ( Polyline polyline ) {
PolylinePoint p = polyline . StartPoint ;
var ret = new RelaxedPolylinePoint ( p , p . Point ) ;
RelaxedPolylinePoint currentRelaxed = ret ;
while ( p . Next ! = null ) {
p = p . Next ;
var r = new RelaxedPolylinePoint ( p , p . Point ) { Prev = currentRelaxed } ;
currentRelaxed . Next = r ;
currentRelaxed = r ;
}
return ret ;
}
void RelaxPolylinePoint ( RelaxedPolylinePoint relaxedPoint ) {
if ( relaxedPoint . PolylinePoint . Prev . Prev = = null & & SourcePort is CurvePort & &
relaxedPoint . PolylinePoint . Polyline ! = SourceLoosePolyline )
return ;
if ( relaxedPoint . PolylinePoint . Next . Next = = null & & TargetPort is CurvePort & &
relaxedPoint . PolylinePoint . Polyline ! = TargetLoosePolyline )
return ;
for ( double d = OffsetForPolylineRelaxing ;
d > ApproximateComparer . DistanceEpsilon & & ! RelaxWithGivenOffset ( d , relaxedPoint ) ;
d / = 2 ) {
}
}
bool RelaxWithGivenOffset ( double offset , RelaxedPolylinePoint relaxedPoint ) {
Debug . Assert ( offset > ApproximateComparer . DistanceEpsilon ) ; //otherwise we are cycling infinitely here
SetRelaxedPointLocation ( offset , relaxedPoint ) ;
if ( StickingSegmentDoesNotIntersectTightObstacles ( relaxedPoint ) ) {
return true ;
}
PullCloserRelaxedPoint ( relaxedPoint . Prev ) ;
return false ;
}
static void PullCloserRelaxedPoint ( RelaxedPolylinePoint relaxedPolylinePoint ) {
relaxedPolylinePoint . PolylinePoint . Point = 0.2 * relaxedPolylinePoint . OriginalPosition +
0.8 * relaxedPolylinePoint . PolylinePoint . Point ;
}
bool StickingSegmentDoesNotIntersectTightObstacles ( RelaxedPolylinePoint relaxedPoint ) {
return
! PolylineSegmentIntersectsTightHierarchy ( relaxedPoint . PolylinePoint . Point ,
relaxedPoint . Prev . PolylinePoint . Point ) & &
( relaxedPoint . Next = = null | |
! PolylineSegmentIntersectsTightHierarchy ( relaxedPoint . PolylinePoint . Point ,
relaxedPoint . Next . PolylinePoint . Point ) ) ;
}
bool PolylineSegmentIntersectsTightHierarchy ( Point a , Point b ) {
return PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( a , b , ObstacleCalculator . RootOfTightHierarchy ) ;
}
bool PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( Point a , Point b , RectangleNode < Polyline > rect ) {
return PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( new LineSegment ( a , b ) , rect ) ;
}
bool PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( LineSegment ls , RectangleNode < Polyline > rect ) {
if ( ! ls . BoundingBox . Intersects ( rect . Rectangle ) )
return false ;
if ( rect . UserData ! = null ) {
foreach ( IntersectionInfo ii in Curve . GetAllIntersections ( ls , rect . UserData , false ) ) {
if ( ii . Segment1 ! = SourceTightPolyline & & ii . Segment1 ! = TargetTightPolyline )
return true ;
if ( ii . Segment1 = = SourceTightPolyline & & SourcePort is CurvePort )
return true ;
if ( ii . Segment1 = = TargetTightPolyline & & TargetPort is CurvePort )
return true ;
}
return false ;
}
return PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( ls , rect . Left ) | |
PolylineIntersectsPolyRectangleNodeOfTightHierarchy ( ls , rect . Right ) ;
}
internal static List < IntersectionInfo > IntersectionsOfLineAndRectangleNodeOverPolyline ( LineSegment ls ,
RectangleNode < Polyline >
rectNode ) {
var ret = new List < IntersectionInfo > ( ) ;
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , rectNode , ret ) ;
return ret ;
}
static void IntersectionsOfLineAndRectangleNodeOverPolyline ( LineSegment ls , RectangleNode < Polyline > rectNode ,
List < IntersectionInfo > listOfIntersections ) {
if ( rectNode = = null )
return ;
if ( ! ls . BoundingBox . Intersects ( rectNode . Rectangle ) )
return ;
if ( rectNode . UserData ! = null ) {
listOfIntersections . AddRange ( Curve . GetAllIntersections ( ls , rectNode . UserData , true ) ) ;
return ;
}
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , rectNode . Left , listOfIntersections ) ;
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , rectNode . Right , listOfIntersections ) ;
}
bool LineCanBeAcceptedForRouting ( LineSegment ls ) {
bool sourceIsFloating = SourcePort is FloatingPort ;
bool targetIsFloating = TargetPort is FloatingPort ;
if ( ! sourceIsFloating & & ! targetIsInsideOfSourceTightPolyline )
if ( ! InsideOfTheAllowedConeOfBoundaryPort ( ls . End , SourcePort as CurvePort ) )
return false ;
if ( ! targetIsFloating & & TargetPort ! = null & & ! sourceIsInsideOfTargetTightPolyline )
if ( ! InsideOfTheAllowedConeOfBoundaryPort ( ls . Start , TargetPort as CurvePort ) )
return false ;
List < IntersectionInfo > xx = IntersectionsOfLineAndRectangleNodeOverPolyline ( ls ,
ObstacleCalculator .
RootOfTightHierarchy ) ;
foreach ( IntersectionInfo ii in xx ) {
if ( ii . Segment1 = = SourceTightPolyline )
continue ;
if ( ii . Segment1 = = targetTightPolyline )
continue ;
return false ;
}
return true ;
}
bool InsideOfTheAllowedConeOfBoundaryPort ( Point pointToTest , CurvePort port ) {
ICurve boundaryCurve = port . Curve ;
bool curveIsClockwise = InteractiveObstacleCalculator . CurveIsClockwise ( boundaryCurve ,
PointInsideOfConvexCurve (
boundaryCurve ) ) ;
Point portLocation = port . Location ;
Point pointOnTheRightConeSide = GetPointOnTheRightBoundaryPortConeSide ( portLocation , boundaryCurve ,
curveIsClockwise , port . Parameter ) ;
Point pointOnTheLeftConeSide = GetPointOnTheLeftBoundaryPortConeSide ( portLocation , boundaryCurve ,
curveIsClockwise , port . Parameter ) ;
return Point . GetTriangleOrientation ( portLocation , pointOnTheRightConeSide , pointToTest ) ! =
TriangleOrientation . Clockwise & &
Point . GetTriangleOrientation ( portLocation , pointToTest , pointOnTheLeftConeSide ) ! =
TriangleOrientation . Clockwise ;
}
Point GetPointOnTheRightBoundaryPortConeSide ( Point portLocation , ICurve boundaryCurve , bool curveIsClockwise ,
double portParam ) {
Point tan = curveIsClockwise
? boundaryCurve . RightDerivative ( portParam )
: - boundaryCurve . LeftDerivative ( portParam ) ;
return portLocation + tan . Rotate ( EnteringAngleBound ) ;
}
Point GetPointOnTheLeftBoundaryPortConeSide ( Point portLocation , ICurve boundaryCurve , bool curveIsClockwise ,
double portParam ) {
Point tan = curveIsClockwise
? - boundaryCurve . LeftDerivative ( portParam )
: boundaryCurve . RightDerivative ( portParam ) ;
return portLocation + tan . Rotate ( - EnteringAngleBound ) ;
}
static void SetRelaxedPointLocation ( double offset , RelaxedPolylinePoint relaxedPoint ) {
bool leftTurn =
Point . GetTriangleOrientation ( relaxedPoint . Next . OriginalPosition , relaxedPoint . OriginalPosition ,
relaxedPoint . Prev . OriginalPosition ) = = TriangleOrientation . Counterclockwise ;
Point v =
( ( relaxedPoint . Next . OriginalPosition - relaxedPoint . Prev . OriginalPosition ) . Normalize ( ) * offset ) . Rotate (
Math . PI / 2 ) ;
if ( ! leftTurn )
v = - v ;
relaxedPoint . PolylinePoint . Point = relaxedPoint . OriginalPosition + v ;
}
2020-06-04 01:36:55 +01:00
// #if TEST_MSAGL
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// // ReSharper disable UnusedMember.Local
// internal void ShowPolylineAndObstacles(params ICurve[] curves)
// {
// // ReSharper restore UnusedMember.Local
// IEnumerable<DebugCurve> ls = GetDebugCurves(curves);
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(ls);
// }
//
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// IEnumerable<DebugCurve> GetDebugCurves(params ICurve[] curves)
// {
// var ls = CreateListWithObstaclesAndPolyline(curves);
// //ls.AddRange(this.VisibilityGraph.Edges.Select(e => new DebugCurve(100,0.1, e is TollFreeVisibilityEdge?"red":"green", new LineSegment(e.SourcePoint, e.TargetPoint))));
// if (_sourceVisibilityVertex != null)
// ls.Add(new DebugCurve("red", CurveFactory.CreateDiamond(4, 4, _sourceVisibilityVertex.Point)));
// if (targetVisibilityVertex != null)
// ls.Add(new DebugCurve("purple", new Ellipse(4, 4, targetVisibilityVertex.Point)));
// var anywerePort=targetPort as HookUpAnywhereFromInsidePort;
// if (anywerePort != null)
// ls.Add(new DebugCurve("purple", anywerePort.LoosePolyline));
// return ls;
// }
//
//
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// List<DebugCurve> CreateListWithObstaclesAndPolyline(params ICurve[] curves)
// {
// var ls = new List<DebugCurve>(ObstacleCalculator.RootOfLooseHierarchy.GetAllLeaves().Select(e => new DebugCurve(100,0.01, "green", e)));
// ls.AddRange(curves.Select(c=>new DebugCurve(100,0.01,"red", c)));
// ls.AddRange(ObstacleCalculator.RootOfTightHierarchy.GetAllLeaves().Select(e => new DebugCurve(100, 0.01, "blue", e)));
//
// // ls.AddRange(visibilityGraph.Edges.Select(e => (ICurve) new LineSegment(e.SourcePoint, e.TargetPoint)));
// if (_polyline != null)
// ls.Add(new DebugCurve(100, 0.03, "blue",_polyline));
// return ls;
// }
// #endif
2020-04-14 00:42:04 +01:00
/// <summary>
/// smoothing the corners of the polyline
/// </summary>
/// <param name="edgePolyline"></param>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline")]
public void SmoothCorners ( SmoothedPolyline edgePolyline ) {
ValidateArg . IsNotNull ( edgePolyline , "edgePolyline" ) ;
Site a = edgePolyline . HeadSite ; //the corner start
Site b ; //the corner origin
Site c ; //the corner other end
while ( Curve . FindCorner ( a , out b , out c ) )
a = SmoothOneCorner ( a , c , b ) ;
}
Site SmoothOneCorner ( Site a , Site c , Site b ) {
if ( CacheCorners ) {
double p , n ;
if ( FindCachedCorner ( a , b , c , out p , out n ) ) {
b . PreviousBezierSegmentFitCoefficient = p ;
b . NextBezierSegmentFitCoefficient = n ;
return b ;
}
}
const double mult = 1.5 ;
const double kMin = 0.01 ;
double k = 0.5 ;
CubicBezierSegment seg ;
double u , v ;
if ( a . Previous = = null ) {
//this will allow to the segment to start from site "a"
u = 2 ;
v = 1 ;
} else if ( c . Next = = null ) {
u = 1 ;
v = 2 ; //this will allow to the segment to end at site "c"
} else
u = v = 1 ;
do {
seg = Curve . CreateBezierSeg ( k * u , k * v , a , b , c ) ;
b . PreviousBezierSegmentFitCoefficient = k * u ;
b . NextBezierSegmentFitCoefficient = k * v ;
k / = mult ;
} while ( ObstacleCalculator . ObstaclesIntersectICurve ( seg ) & & k > kMin ) ;
k * = mult ; //that was the last k
if ( k < 0.5 & & k > kMin ) {
//one time try a smoother seg
k = 0.5 * ( k + k * mult ) ;
seg = Curve . CreateBezierSeg ( k * u , k * v , a , b , c ) ;
if ( ! ObstacleCalculator . ObstaclesIntersectICurve ( seg ) ) {
b . PreviousBezierSegmentFitCoefficient = k * u ;
b . NextBezierSegmentFitCoefficient = k * v ;
}
}
if ( CacheCorners )
CacheCorner ( a , b , c ) ;
return b ;
}
internal int foundCachedCorners ;
bool FindCachedCorner ( Site a , Site b , Site c , out double prev , out double next ) {
Corner corner = new Corner ( a . Point , b . Point , c . Point ) ;
Tuple < double , double > prevNext ;
if ( cornerTable . TryGetValue ( corner , out prevNext ) ) {
if ( a . Point = = corner . a ) {
prev = prevNext . Item1 ;
next = prevNext . Item2 ;
}
else {
prev = prevNext . Item2 ;
next = prevNext . Item1 ;
}
foundCachedCorners + + ;
return true ;
}
prev = next = 0 ;
return false ;
}
void CacheCorner ( Site a , Site b , Site c ) {
cornerTable [ new Corner ( a . Point , b . Point , c . Point ) ] = new Tuple < double , double > ( b . PreviousBezierSegmentFitCoefficient , b . NextBezierSegmentFitCoefficient ) ;
}
/// <summary>
/// is set to true will cache three points defining the corner
/// to avoid obstacle avoidance calculation
/// </summary>
public bool CacheCorners {
get { return cacheCorners ; }
set {
cacheCorners = value ;
if ( cacheCorners )
cornerTable = new Dictionary < Corner , Tuple < double , double > > ( ) ;
else {
if ( cornerTable ! = null )
cornerTable . Clear ( ) ;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="underlyingPolyline"></param>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline")]
public void TryToRemoveInflectionsAndCollinearSegments ( SmoothedPolyline underlyingPolyline ) {
ValidateArg . IsNotNull ( underlyingPolyline , "underlyingPolyline" ) ;
bool progress = true ;
while ( progress ) {
progress = false ;
for ( Site s = underlyingPolyline . HeadSite ; s ! = null & & s . Next ! = null ; s = s . Next ) {
if ( s . Turn * s . Next . Turn < 0 )
progress = TryToRemoveInflectionEdge ( ref s ) | | progress ;
}
}
}
bool TryToRemoveInflectionEdge ( ref Site s ) {
if ( ! ObstacleCalculator . ObstaclesIntersectLine ( s . Previous . Point , s . Next . Point ) ) {
Site a = s . Previous ; //forget s
Site b = s . Next ;
a . Next = b ;
b . Previous = a ;
s = a ;
return true ;
}
if ( ! ObstacleCalculator . ObstaclesIntersectLine ( s . Previous . Point , s . Next . Next . Point ) ) {
//forget about s and s.Next
Site a = s . Previous ;
Site b = s . Next . Next ;
a . Next = b ;
b . Previous = a ;
s = a ;
return true ;
}
if ( ! ObstacleCalculator . ObstaclesIntersectLine ( s . Point , s . Next . Next . Point ) ) {
//forget about s.Next
Site b = s . Next . Next ;
s . Next = b ;
b . Previous = s ;
return true ;
}
return false ;
}
//internal Point TargetPoint {
// get {
// CurvePort tp = this.TargetPort as CurvePort;
// if (tp != null)
// return this.Target.BoundaryCurve[tp.Parameter];
// else
// return (this.TargetPort as FloatingPort).Location;
// }
//}
//internal Point SourcePoint {
// get {
// CurvePort sp = this.SourcePort as CurvePort;
// if (sp != null)
// return this.Source.BoundaryCurve[sp.Parameter];
// else
// return (this.SourcePort as FloatingPort).Location;
// }
//}
Polyline GetShortestPolyline ( VisibilityVertex sourceVisVertex , VisibilityVertex _targetVisVertex ) {
CleanTheGraphForShortestPath ( ) ;
var pathCalc = new SingleSourceSingleTargetShortestPathOnVisibilityGraph ( this . visibilityGraph , sourceVisVertex , _targetVisVertex ) ;
IEnumerable < VisibilityVertex > path = pathCalc . GetPath ( UseEdgeLengthMultiplier ) ;
if ( path = = null ) {
//ShowIsPassable(_sourceVisibilityVertex, _targetVisVertex);
return null ;
}
Debug . Assert ( path . First ( ) = = sourceVisVertex & & path . Last ( ) = = _targetVisVertex ) ;
var ret = new Polyline ( ) ;
foreach ( VisibilityVertex v in path )
ret . AddPoint ( v . Point ) ;
return RemoveCollinearVertices ( ret ) ;
}
2020-06-04 01:36:55 +01:00
// #if TEST_MSAGL
// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
// private void ShowIsPassable(VisibilityVertex sourceVisVertex, VisibilityVertex targetVisVertex)
// {
// var dd = new List<DebugCurve>(
// visibilityGraph.Edges.Select(
// e =>
// new DebugCurve(100, 0.5, e.IsPassable == null || e.IsPassable() ? "green" : "red",
// new LineSegment(e.SourcePoint, e.TargetPoint))));
// if(sourceVisVertex!=null)
// dd.Add(new DebugCurve(CurveFactory.CreateDiamond(3, 3, sourceVisVertex.Point)));
// if(targetVisVertex!=null)
// dd.Add(new DebugCurve(CurveFactory.CreateEllipse(3, 3, targetVisVertex.Point)));
//
// if (Obstacles != null)
// dd.AddRange(Obstacles.Select(o => new DebugCurve(o)));
// LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(dd);
// }
// #endif
2020-04-14 00:42:04 +01:00
void CleanTheGraphForShortestPath ( ) {
visibilityGraph . ClearPrevEdgesTable ( ) ;
}
internal static Polyline RemoveCollinearVertices ( Polyline ret ) {
for ( PolylinePoint pp = ret . StartPoint . Next ; pp . Next ! = null ; pp = pp . Next ) {
if ( Point . GetTriangleOrientation ( pp . Prev . Point , pp . Point , pp . Next . Point ) = =
TriangleOrientation . Collinear ) {
pp . Prev . Next = pp . Next ;
pp . Next . Prev = pp . Prev ;
}
}
return ret ;
}
/// <summary>
/// returns true if the nodes overlap or just positioned too close
/// </summary>
public bool OverlapsDetected {
get { return ObstacleCalculator . OverlapsDetected ; }
}
///<summary>
///</summary>
public double ConeSpannerAngle { get ; set ; }
internal RectangleNode < Polyline > TightHierarchy {
get { return ObstacleCalculator . RootOfTightHierarchy ; }
set { ObstacleCalculator . RootOfTightHierarchy = value ; }
}
internal RectangleNode < Polyline > LooseHierarchy {
get { return ObstacleCalculator . RootOfLooseHierarchy ; }
set { ObstacleCalculator . RootOfLooseHierarchy = value ; }
}
internal bool UseSpanner { get ; set ; }
void CalculateObstacles ( ) {
ObstacleCalculator = new InteractiveObstacleCalculator ( Obstacles , TightPadding , LoosePadding ,
IgnoreTightPadding ) ;
ObstacleCalculator . Calculate ( ) ;
}
// int count;
/// <summary>
///
/// </summary>
/// <param name="targetLocation"></param>
/// <returns></returns>
public EdgeGeometry RouteEdgeToLocation ( Point targetLocation ) {
TargetPort = new FloatingPort ( ( ICurve ) null , targetLocation ) ; //otherwise route edge to a port would be called
TargetTightPolyline = null ;
TargetLoosePolyline = null ;
var edgeGeometry = new EdgeGeometry ( ) ;
var ls = new LineSegment ( SourcePort . Location , targetLocation ) ;
if ( LineCanBeAcceptedForRouting ( ls ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( ls . Start ) ;
_polyline . AddPoint ( ls . End ) ;
edgeGeometry . SmoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
edgeGeometry . Curve = edgeGeometry . SmoothedPolyline . CreateCurve ( ) ;
return edgeGeometry ;
}
//can we do with just two line segments?
if ( SourcePort is CurvePort ) {
ls = new LineSegment ( StartPointOfEdgeRouting , targetLocation ) ;
if (
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , ObstacleCalculator . RootOfTightHierarchy ) . Count = =
0 ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( SourcePort . Location ) ;
_polyline . AddPoint ( ls . Start ) ;
_polyline . AddPoint ( ls . End ) ;
//RelaxPolyline();
edgeGeometry . SmoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
edgeGeometry . Curve = edgeGeometry . SmoothedPolyline . CreateCurve ( ) ;
return edgeGeometry ;
}
}
ExtendVisibilityGraphToLocation ( targetLocation ) ;
_polyline = GetShortestPolyline ( SourceVisibilityVertex , TargetVisibilityVertex ) ;
RelaxPolyline ( ) ;
if ( SourcePort is CurvePort )
_polyline . PrependPoint ( SourcePort . Location ) ;
edgeGeometry . SmoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
edgeGeometry . Curve = edgeGeometry . SmoothedPolyline . CreateCurve ( ) ;
return edgeGeometry ;
}
/// <summary>
/// routes the edge to the port
/// </summary>
/// <param name="edgeTargetPort"></param>
/// <param name="portLoosePolyline"></param>
/// <param name="smooth"> if true will smooth the edge avoiding the obstacles, will take more time</param>
/// <param name="smoothedPolyline"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline")]
public ICurve RouteEdgeToPort ( Port edgeTargetPort , Polyline portLoosePolyline , bool smooth , out SmoothedPolyline smoothedPolyline ) {
ValidateArg . IsNotNull ( edgeTargetPort , "edgeTargetToPort" ) ;
if ( ! ObstacleCalculator . IsEmpty ( ) ) {
TargetPort = edgeTargetPort ;
TargetTightPolyline = GetFirstHitPolyline ( edgeTargetPort . Location ,
ObstacleCalculator . RootOfTightHierarchy ) ;
Debug . Assert ( targetTightPolyline ! = null ) ;
var bp = edgeTargetPort as CurvePort ;
if ( bp ! = null )
return RouteEdgeToBoundaryPort ( portLoosePolyline , smooth , out smoothedPolyline ) ;
return RouteEdgeToFloatingPortOfNode ( portLoosePolyline , smooth , out smoothedPolyline ) ;
}
if ( sourcePort ! = null & & targetPort ! = null ) {
smoothedPolyline = SmoothedPolylineFromTwoPoints ( sourcePort . Location , targetPort . Location ) ;
return new LineSegment ( sourcePort . Location , targetPort . Location ) ;
}
smoothedPolyline = null ;
return null ;
}
SmoothedPolyline SmoothedPolylineFromTwoPoints ( Point s , Point e ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( s ) ;
_polyline . AddPoint ( e ) ;
return SmoothedPolyline . FromPoints ( _polyline ) ;
}
ICurve RouteEdgeToFloatingPortOfNode ( Polyline portLoosePolyline , bool smooth , out SmoothedPolyline smoothedPolyline ) {
if ( sourcePort is FloatingPort )
return RouteFromFloatingPortToFloatingPort ( portLoosePolyline , smooth , out smoothedPolyline ) ;
return RouteFromBoundaryPortToFloatingPort ( portLoosePolyline , smooth , out smoothedPolyline ) ;
}
ICurve RouteFromBoundaryPortToFloatingPort ( Polyline targetPortLoosePolyline , bool smooth , out SmoothedPolyline polyline ) {
Point sourcePortLocation = SourcePort . Location ;
Point targetPortLocation = targetPort . Location ;
var ls = new LineSegment ( sourcePortLocation , targetPortLocation ) ;
if ( LineCanBeAcceptedForRouting ( ls ) ) {
polyline = SmoothedPolylineFromTwoPoints ( ls . Start , ls . End ) ;
return ls ;
}
if ( ! targetIsInsideOfSourceTightPolyline ) {
//try a variant with two segments
Point takenOutPoint = TakeBoundaryPortOutsideOfItsLoosePolyline ( SourcePort . Curve ,
( ( CurvePort ) SourcePort ) . Parameter ,
SourceLoosePolyline ) ;
ls = new LineSegment ( takenOutPoint , targetPortLocation ) ;
if ( LineAvoidsTightHierarchy ( ls , targetPortLoosePolyline ) ) {
polyline = SmoothedPolylineFromTwoPoints ( ls . Start , ls . End ) ;
return ls ;
}
}
//we need to route throw the visibility graph
ExtendVisibilityGraphToLocationOfTargetFloatingPort ( targetPortLoosePolyline ) ;
_polyline = GetShortestPolyline ( SourceVisibilityVertex , TargetVisibilityVertex ) ;
Polyline tmp = SourceTightPolyline ;
if ( ! targetIsInsideOfSourceTightPolyline )
//this is done to avoid shorcutting through the source tight polyline
SourceTightPolyline = null ;
TryShortcutPolyline ( ) ;
SourceTightPolyline = tmp ;
RelaxPolyline ( ) ;
_polyline . PrependPoint ( sourcePortLocation ) ;
return SmoothCornersAndReturnCurve ( smooth , out polyline ) ;
}
ICurve SmoothCornersAndReturnCurve ( bool smooth , out SmoothedPolyline smoothedPolyline ) {
smoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
if ( smooth )
SmoothCorners ( smoothedPolyline ) ;
return smoothedPolyline . CreateCurve ( ) ;
}
ICurve RouteFromFloatingPortToFloatingPort ( Polyline portLoosePolyline , bool smooth , out SmoothedPolyline smoothedPolyline ) {
Point targetPortLocation = TargetPort . Location ;
var ls = new LineSegment ( StartPointOfEdgeRouting , targetPortLocation ) ;
if ( AllowedShootingStraightLines & & LineAvoidsTightHierarchy ( ls , SourceTightPolyline , targetTightPolyline ) ) {
smoothedPolyline = SmoothedPolylineFromTwoPoints ( ls . Start , ls . End ) ;
return ls ;
}
//we need to route through the visibility graph
ExtendVisibilityGraphToLocationOfTargetFloatingPort ( portLoosePolyline ) ;
_polyline = GetShortestPolyline ( SourceVisibilityVertex , TargetVisibilityVertex ) ;
if ( _polyline = = null ) {
smoothedPolyline = null ;
return null ;
}
if ( UseSpanner )
TryShortcutPolyline ( ) ;
RelaxPolyline ( ) ;
smoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
return SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
}
void TryShortcutPolyline ( ) {
if ( UseInnerPolylingShortcutting )
while ( ShortcutPolylineOneTime ( ) ) { }
if ( UsePolylineEndShortcutting )
TryShortCutThePolylineEnds ( ) ;
}
void TryShortCutThePolylineEnds ( ) {
TryShortcutPolylineStart ( ) ;
TryShortcutPolylineEnd ( ) ;
}
void TryShortcutPolylineEnd ( ) {
PolylinePoint a = _polyline . EndPoint ;
PolylinePoint b = a . Prev ;
if ( b = = null ) return ;
PolylinePoint c = b . Prev ;
if ( c = = null ) return ;
Point m = 0.5 * ( b . Point + c . Point ) ;
if ( LineAvoidsTightHierarchy ( a . Point , m , _sourceTightPolyline , targetTightPolyline ) ) {
var p = new PolylinePoint ( m ) { Next = a , Prev = c } ;
a . Prev = p ;
c . Next = p ;
}
}
void TryShortcutPolylineStart ( ) {
PolylinePoint a = _polyline . StartPoint ;
PolylinePoint b = a . Next ;
if ( b = = null ) return ;
PolylinePoint c = b . Next ;
if ( c = = null ) return ;
Point m = 0.5 * ( b . Point + c . Point ) ;
if ( LineAvoidsTightHierarchy ( a . Point , m , _sourceTightPolyline , targetTightPolyline ) ) {
var p = new PolylinePoint ( m ) { Prev = a , Next = c } ;
a . Next = p ;
c . Prev = p ;
}
}
bool ShortcutPolylineOneTime ( ) {
bool ret = false ;
for ( PolylinePoint pp = _polyline . StartPoint ; pp . Next ! = null & & pp . Next . Next ! = null ; pp = pp . Next )
ret | = TryShortcutPolyPoint ( pp ) ;
return ret ;
}
bool TryShortcutPolyPoint ( PolylinePoint pp ) {
if ( LineAvoidsTightHierarchy ( new LineSegment ( pp . Point , pp . Next . Next . Point ) , SourceTightPolyline ,
targetTightPolyline ) ) {
// if (debug)
// ShowPolylineAndObstaclesWithLineSeg(new LineSegment(pp.Point, pp.Next.Next.Point));
//remove pp.Next
pp . Next = pp . Next . Next ;
pp . Next . Prev = pp ;
return true ;
}
return false ;
}
/ *
void ShowPolylineAndObstaclesWithLineSeg ( LineSegment lineSegment )
{
var ls = GetDebugCurves ( ) ;
ls . Add ( new DebugCurve ( 2 , "pink" , lineSegment ) ) ;
LayoutAlgorithmSettings . ShowDebugCurvesEnumeration ( ls ) ;
}
* /
void ExtendVisibilityGraphToLocationOfTargetFloatingPort ( Polyline portLoosePolyline ) {
if ( VisibilityGraph = = null )
VisibilityGraph = new VisibilityGraph ( ) ;
List < Polygon > addedPolygons = null ;
Point targetLocation = targetPort . Location ;
if ( ! activeRectangle . Contains ( targetLocation ) ) {
if ( activeRectangle . IsEmpty )
activeRectangle = new Rectangle ( SourcePort . Location , targetLocation ) ;
else
activeRectangle . Add ( targetLocation ) ;
addedPolygons = GetAddedPolygonesAndMaybeExtendActiveRectangle ( ) ;
foreach ( Polygon polygon in addedPolygons )
VisibilityGraph . AddHole ( polygon . Polyline ) ;
}
if ( addedPolygons = = null ) {
if ( targetVisibilityVertex ! = null )
VisibilityGraph . RemoveVertex ( targetVisibilityVertex ) ;
CalculateEdgeTargetVisibilityGraphForFloatingPort ( targetLocation , portLoosePolyline ) ;
if ( SourceVisibilityVertex = = null )
CalculateSourcePortVisibilityGraph ( ) ;
}
else {
RemovePointVisibilityGraphs ( ) ;
var visibilityGraphGenerator = new InteractiveTangentVisibilityGraphCalculator ( addedPolygons ,
activePolygons ,
VisibilityGraph ) ;
visibilityGraphGenerator . Run ( ) ;
activePolygons . AddRange ( addedPolygons ) ;
CalculateEdgeTargetVisibilityGraphForFloatingPort ( targetLocation , portLoosePolyline ) ;
CalculateSourcePortVisibilityGraph ( ) ;
}
}
void CalculateEdgeTargetVisibilityGraphForFloatingPort ( Point targetLocation , Polyline targetLoosePoly ) {
if ( UseSpanner )
targetVisibilityVertex = AddTransientVisibilityEdgesForPort ( targetLocation , targetLoosePoly ) ;
else
PointVisibilityCalculator . CalculatePointVisibilityGraph (
GetActivePolylinesWithException ( targetLoosePoly ) , VisibilityGraph , targetLocation ,
VisibilityKind . Tangent , out targetVisibilityVertex ) ;
}
VisibilityVertex AddTransientVisibilityEdgesForPort ( Point point , IEnumerable < Point > loosePoly ) {
VisibilityVertex v = GetVertex ( point ) ;
if ( v ! = null )
return v ;
v = visibilityGraph . AddVertex ( point ) ;
if ( loosePoly ! = null ) //if the edges have not been calculated do it in a quick and dirty mode
foreach ( Point p in loosePoly )
visibilityGraph . AddEdge ( point , p , ( ( a , b ) = > new TollFreeVisibilityEdge ( a , b ) ) ) ;
else {
PointVisibilityCalculator . CalculatePointVisibilityGraph ( GetActivePolylines ( ) ,
VisibilityGraph , point ,
VisibilityKind . Tangent ,
out v ) ;
Debug . Assert ( v ! = null ) ;
}
return v ;
}
VisibilityVertex GetVertex ( Point point ) {
VisibilityVertex v = visibilityGraph . FindVertex ( point ) ;
if ( v = = null & & LookForRoundedVertices )
v = visibilityGraph . FindVertex ( ApproximateComparer . Round ( point ) ) ;
return v ;
}
internal bool LookForRoundedVertices { get ; set ; }
internal InteractiveObstacleCalculator ObstacleCalculator { get ; set ; }
///<summary>
///</summary>
///<param name="obstacles"></param>
///<param name="padding"></param>
///<param name="loosePadding"></param>
///<param name="coneSpannerAngle"></param>
///<param name="ignoreTightPadding"></param>
public InteractiveEdgeRouter ( IEnumerable < ICurve > obstacles , double padding , double loosePadding , double coneSpannerAngle , bool ignoreTightPadding ) {
IgnoreTightPadding = ignoreTightPadding ;
EnteringAngleBound = 80 * Math . PI / 180 ;
TightPadding = padding ;
LoosePadding = loosePadding ;
OffsetForPolylineRelaxing = 0.75 * padding ;
if ( coneSpannerAngle > 0 ) {
Debug . Assert ( coneSpannerAngle > Math . PI / 180 ) ;
Debug . Assert ( coneSpannerAngle < = 90 * Math . PI / 180 ) ;
UseSpanner = true ;
ExpectedProgressSteps = ConeSpanner . GetTotalSteps ( coneSpannerAngle ) ;
} else {
ExpectedProgressSteps = obstacles . Count ( ) ;
}
ConeSpannerAngle = coneSpannerAngle ;
Obstacles = obstacles ;
CalculateObstacles ( ) ;
}
internal bool IgnoreTightPadding { get ; set ; }
IEnumerable < Polyline > GetActivePolylinesWithException ( Polyline targetLoosePoly ) {
return from polygon in activePolygons where polygon . Polyline ! = targetLoosePoly select polygon . Polyline ;
}
ICurve RouteEdgeToBoundaryPort ( Polyline portLoosePolyline , bool smooth , out SmoothedPolyline smoothedPolyline ) {
TargetLoosePolyline = portLoosePolyline ;
if ( sourcePort is FloatingPort )
return RouteFromFloatingPortToBoundaryPort ( smooth , out smoothedPolyline ) ;
return RouteFromBoundaryPortToBoundaryPort ( smooth , out smoothedPolyline ) ;
}
ICurve RouteFromBoundaryPortToBoundaryPort ( bool smooth , out SmoothedPolyline smoothedPolyline ) {
Point sourcePortLocation = SourcePort . Location ;
ICurve curve ;
Point targetPortLocation = targetPort . Location ;
var ls = new LineSegment ( sourcePortLocation , targetPortLocation ) ;
if ( LineCanBeAcceptedForRouting ( ls ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( ls . Start ) ;
_polyline . AddPoint ( ls . End ) ;
smoothedPolyline = SmoothedPolylineFromTwoPoints ( ls . Start , ls . End ) ;
curve = SmoothedPolyline . FromPoints ( _polyline ) . CreateCurve ( ) ;
} else {
//try three variants with two segments
Point takenOutPoint = TakeBoundaryPortOutsideOfItsLoosePolyline ( targetPort . Curve ,
( ( CurvePort ) targetPort ) . Parameter ,
TargetLoosePolyline ) ;
ls = new LineSegment ( sourcePortLocation , takenOutPoint ) ;
if ( InsideOfTheAllowedConeOfBoundaryPort ( takenOutPoint , SourcePort as CurvePort ) & &
LineAvoidsTightHierarchy ( ls , _sourceTightPolyline ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( ls . Start ) ;
_polyline . AddPoint ( ls . End ) ;
_polyline . AddPoint ( targetPortLocation ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
} else {
ls = new LineSegment ( StartPointOfEdgeRouting , targetPortLocation ) ;
if ( InsideOfTheAllowedConeOfBoundaryPort ( StartPointOfEdgeRouting , TargetPort as CurvePort ) & &
LineAvoidsTightHierarchy ( ls ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( sourcePortLocation ) ;
_polyline . AddPoint ( ls . Start ) ;
_polyline . AddPoint ( ls . End ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
} else {
// we still can make the polyline with two segs when the port sticking segs are intersecting
Point x ;
if ( LineSegment . Intersect ( sourcePortLocation , StartPointOfEdgeRouting , targetPortLocation ,
takenOutPoint , out x ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( sourcePortLocation ) ;
_polyline . AddPoint ( x ) ;
_polyline . AddPoint ( targetPortLocation ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
} else if ( ApproximateComparer . Close ( StartPointOfEdgeRouting , takenOutPoint ) ) {
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( sourcePortLocation ) ;
_polyline . AddPoint ( takenOutPoint ) ;
_polyline . AddPoint ( targetPortLocation ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
} else if ( LineAvoidsTightHierarchy ( new LineSegment ( StartPointOfEdgeRouting , takenOutPoint ) ) ) {
//can we do three segments?
_polyline = new Polyline ( ) ;
_polyline . AddPoint ( sourcePortLocation ) ;
_polyline . AddPoint ( StartPointOfEdgeRouting ) ;
_polyline . AddPoint ( takenOutPoint ) ;
_polyline . AddPoint ( targetPortLocation ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
} else {
ExtendVisibilityGraphToTargetBoundaryPort ( takenOutPoint ) ;
_polyline = GetShortestPolyline ( SourceVisibilityVertex , TargetVisibilityVertex ) ;
Polyline tmpTargetTight ;
Polyline tmpSourceTight = HideSourceTargetTightsIfNeeded ( out tmpTargetTight ) ;
TryShortcutPolyline ( ) ;
RecoverSourceTargetTights ( tmpSourceTight , tmpTargetTight ) ;
RelaxPolyline ( ) ;
_polyline . PrependPoint ( sourcePortLocation ) ;
_polyline . AddPoint ( targetPortLocation ) ;
curve = SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
}
}
}
}
return curve ;
}
void RecoverSourceTargetTights ( Polyline tmpSourceTight , Polyline tmpTargetTight ) {
SourceTightPolyline = tmpSourceTight ;
TargetTightPolyline = tmpTargetTight ;
}
Polyline HideSourceTargetTightsIfNeeded ( out Polyline tmpTargetTight ) {
Polyline tmpSourceTight = SourceTightPolyline ;
tmpTargetTight = TargetTightPolyline ;
SourceTightPolyline = TargetTightPolyline = null ;
return tmpSourceTight ;
}
bool LineAvoidsTightHierarchy ( LineSegment lineSegment ) {
return
IntersectionsOfLineAndRectangleNodeOverPolyline ( lineSegment , ObstacleCalculator . RootOfTightHierarchy ) .
Count = = 0 ;
}
ICurve RouteFromFloatingPortToBoundaryPort ( bool smooth , out SmoothedPolyline smoothedPolyline ) {
Point targetPortLocation = targetPort . Location ;
LineSegment ls ;
if ( InsideOfTheAllowedConeOfBoundaryPort ( sourcePort . Location , ( CurvePort ) targetPort ) ) {
ls = new LineSegment ( SourcePort . Location , targetPortLocation ) ;
if ( LineCanBeAcceptedForRouting ( ls ) ) {
smoothedPolyline = SmoothedPolylineFromTwoPoints ( ls . Start , ls . End ) ;
return ls ;
}
}
Point takenOutTargetPortLocation = TakeBoundaryPortOutsideOfItsLoosePolyline ( TargetPort . Curve ,
( ( CurvePort ) TargetPort ) .
Parameter ,
TargetLoosePolyline ) ;
//can we do with just two line segments?
ls = new LineSegment ( SourcePort . Location , takenOutTargetPortLocation ) ;
if ( LineAvoidsTightHierarchy ( ls , _sourceTightPolyline ) ) {
_polyline = new Polyline ( ls . Start , ls . End , targetPortLocation ) ;
smoothedPolyline = SmoothedPolyline . FromPoints ( _polyline ) ;
return smoothedPolyline . CreateCurve ( ) ;
}
ExtendVisibilityGraphToTargetBoundaryPort ( takenOutTargetPortLocation ) ;
_polyline = GetShortestPolyline ( SourceVisibilityVertex , TargetVisibilityVertex ) ;
RelaxPolyline ( ) ;
_polyline . AddPoint ( targetPortLocation ) ;
return SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
}
bool LineAvoidsTightHierarchy ( LineSegment ls , Polyline polylineToExclude ) {
bool lineIsGood = true ;
foreach ( IntersectionInfo ii in
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , ObstacleCalculator . RootOfTightHierarchy ) )
if ( ii . Segment1 ! = polylineToExclude ) {
lineIsGood = false ;
break ;
}
return lineIsGood ;
}
bool LineAvoidsTightHierarchy ( LineSegment ls , Polyline polylineToExclude0 , Polyline polylineToExclude1 ) {
bool lineIsGood = true ;
foreach ( IntersectionInfo ii in
IntersectionsOfLineAndRectangleNodeOverPolyline ( ls , ObstacleCalculator . RootOfTightHierarchy ) )
if ( ! ( ii . Segment1 = = polylineToExclude0 | | ii . Segment1 = = polylineToExclude1 ) ) {
lineIsGood = false ;
break ;
}
return lineIsGood ;
}
bool LineAvoidsTightHierarchy ( Point a , Point b , Polyline polylineToExclude0 , Polyline polylineToExclude1 ) {
return LineAvoidsTightHierarchy ( new LineSegment ( a , b ) , polylineToExclude0 , polylineToExclude1 ) ;
}
void ExtendVisibilityGraphToTargetBoundaryPort ( Point takenOutTargetPortLocation ) {
List < Polygon > addedPolygons = null ;
if ( VisibilityGraph = = null )
VisibilityGraph = new VisibilityGraph ( ) ;
if ( ! activeRectangle . Contains ( takenOutTargetPortLocation ) | |
! activeRectangle . Contains ( TargetLoosePolyline . BoundingBox ) ) {
if ( activeRectangle . IsEmpty ) {
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 there are no structs in js
activeRectangle = TargetLoosePolyline . BoundingBox . Clone ( ) ;
#else
activeRectangle = TargetLoosePolyline . BoundingBox ;
#endif
activeRectangle . Add ( SourcePort . Location ) ;
activeRectangle . Add ( StartPointOfEdgeRouting ) ;
activeRectangle . Add ( takenOutTargetPortLocation ) ;
} else {
activeRectangle . Add ( takenOutTargetPortLocation ) ;
activeRectangle . Add ( TargetLoosePolyline . BoundingBox ) ;
}
addedPolygons = GetAddedPolygonesAndMaybeExtendActiveRectangle ( ) ;
foreach ( Polygon polygon in addedPolygons )
VisibilityGraph . AddHole ( polygon . Polyline ) ;
}
if ( addedPolygons = = null ) {
if ( targetVisibilityVertex ! = null )
VisibilityGraph . RemoveVertex ( targetVisibilityVertex ) ;
CalculateEdgeTargetVisibilityGraph ( takenOutTargetPortLocation ) ;
} else {
RemovePointVisibilityGraphs ( ) ;
var visibilityGraphGenerator = new InteractiveTangentVisibilityGraphCalculator ( addedPolygons ,
activePolygons ,
VisibilityGraph ) ;
visibilityGraphGenerator . Run ( ) ;
activePolygons . AddRange ( addedPolygons ) ;
CalculateEdgeTargetVisibilityGraph ( takenOutTargetPortLocation ) ;
CalculateSourcePortVisibilityGraph ( ) ;
}
}
/// <summary>
/// returns the hit object
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline")]
public Polyline GetHitLoosePolyline ( Point point ) {
if ( ObstacleCalculator . IsEmpty ( ) | | ObstacleCalculator . RootOfLooseHierarchy = = null )
return null ;
return GetFirstHitPolyline ( point , ObstacleCalculator . RootOfLooseHierarchy ) ;
}
internal static Polyline GetFirstHitPolyline ( Point point , RectangleNode < Polyline > rectangleNode ) {
RectangleNode < Polyline > rectNode = GetFirstHitRectangleNode ( point , rectangleNode ) ;
return rectNode ! = null ? rectNode . UserData : null ;
}
static RectangleNode < Polyline > GetFirstHitRectangleNode ( Point point , RectangleNode < Polyline > rectangleNode ) {
if ( rectangleNode = = null )
return null ;
return rectangleNode . FirstHitNode ( point ,
( pnt , polyline ) = >
Curve . PointRelativeToCurveLocation ( pnt , polyline ) ! = PointLocation . Outside
? HitTestBehavior . Stop
: HitTestBehavior . Continue ) ;
}
/// <summary>
///
/// </summary>
public void Clean ( ) {
SourcePort = TargetPort = null ;
SourceLoosePolyline = SourceTightPolyline = null ;
targetTightPolyline = TargetLoosePolyline = null ;
VisibilityGraph = null ;
_sourceVisibilityVertex = targetVisibilityVertex = null ;
activePolygons . Clear ( ) ;
alreadyAddedOrExcludedPolylines . Clear ( ) ;
activeRectangle . SetToEmpty ( ) ;
}
/// <summary>
/// setting source port and the loose polyline of the port
/// </summary>
/// <param name="port"></param>
/// <param name="sourceLoosePolylinePar"></param>
[ SuppressMessage ( "Microsoft.Naming" , "CA1704:IdentifiersShouldBeSpelledCorrectly" , MessageId = "polyline" ) ,
SuppressMessage ( "Microsoft.Naming" , "CA1704:IdentifiersShouldBeSpelledCorrectly" , MessageId = "Polyline" ) ]
public void SetSourcePortAndSourceLoosePolyline ( Port port , Polyline sourceLoosePolylinePar ) {
SourceLoosePolyline = sourceLoosePolylinePar ;
sourcePort = port ;
if ( sourcePort ! = null ) {
SourceTightPolyline = GetFirstHitPolyline ( sourcePort . Location , ObstacleCalculator . RootOfTightHierarchy ) ;
if ( sourcePort is FloatingPort ) {
alreadyAddedOrExcludedPolylines . Insert ( SourceLoosePolyline ) ;
//we need to exclude the loose polyline around the source port from the tangent visibily graph
StartPointOfEdgeRouting = SourcePort . Location ;
}
else
StartPointOfEdgeRouting = TakeBoundaryPortOutsideOfItsLoosePolyline ( SourcePort . Curve ,
( ( CurvePort ) sourcePort ) .
Parameter ,
SourceLoosePolyline ) ;
}
}
/// <summary>
///
/// </summary>
protected override void RunInternal ( ) {
CalculateWholeTangentVisibilityGraph ( ) ;
}
internal void CalculateWholeTangentVisibilityGraph ( ) {
VisibilityGraph = new VisibilityGraph ( ) ;
CalculateWholeVisibilityGraphOnExistingGraph ( ) ;
}
internal void CalculateWholeVisibilityGraphOnExistingGraph ( ) {
activePolygons = new List < Polygon > ( AllPolygons ( ) ) ;
foreach ( Polyline polylineLocal in ObstacleCalculator . LooseObstacles )
VisibilityGraph . AddHole ( polylineLocal ) ;
AlgorithmBase visibilityGraphGenerator ;
if ( UseSpanner ) {
visibilityGraphGenerator = new ConeSpanner ( ObstacleCalculator . LooseObstacles , VisibilityGraph )
{ ConeAngle = ConeSpannerAngle } ;
}
else {
visibilityGraphGenerator = new InteractiveTangentVisibilityGraphCalculator ( new List < Polygon > ( ) ,
activePolygons ,
visibilityGraph ) ;
}
visibilityGraphGenerator . Run ( ) ;
}
///<summary>
///</summary>
///<param name="sourcePortLocal"></param>
///<param name="targetPortLocal"></param>
///<param name="smooth"></param>
///<param name="smoothedPolyline"></param>
///<returns></returns>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
public ICurve RouteSplineFromPortToPortWhenTheWholeGraphIsReady ( Port sourcePortLocal ,
Port targetPortLocal , bool smooth , out SmoothedPolyline smoothedPolyline ) {
bool reversed = ( sourcePortLocal is FloatingPort & & targetPortLocal is CurvePort )
| | sourcePortLocal is HookUpAnywhereFromInsidePort ;
if ( reversed ) {
Port tmp = sourcePortLocal ;
sourcePortLocal = targetPortLocal ;
targetPortLocal = tmp ;
}
sourcePort = sourcePortLocal ;
targetPort = targetPortLocal ;
FigureOutSourceTargetPolylinesAndActiveRectangle ( ) ;
ICurve curve = GetEdgeGeomByRouting ( smooth , out smoothedPolyline ) ;
if ( curve = = null ) return null ;
_sourceVisibilityVertex = targetVisibilityVertex = null ;
if ( reversed )
curve = curve . Reverse ( ) ;
return curve ;
}
ICurve GetEdgeGeomByRouting ( bool smooth , out SmoothedPolyline smoothedPolyline ) {
targetIsInsideOfSourceTightPolyline = SourceTightPolyline = = null | |
Curve . PointRelativeToCurveLocation ( targetPort . Location ,
SourceTightPolyline ) = =
PointLocation . Inside ;
sourceIsInsideOfTargetTightPolyline = TargetTightPolyline = = null | |
Curve . PointRelativeToCurveLocation ( sourcePort . Location ,
TargetTightPolyline ) = =
PointLocation . Inside ;
var curvePort = sourcePort as CurvePort ;
ICurve curve ;
if ( curvePort ! = null ) {
StartPointOfEdgeRouting = ! targetIsInsideOfSourceTightPolyline
? TakeBoundaryPortOutsideOfItsLoosePolyline ( curvePort . Curve ,
curvePort . Parameter ,
SourceLoosePolyline )
: curvePort . Location ;
CalculateSourcePortVisibilityGraph ( ) ;
if ( targetPort is CurvePort )
curve = RouteFromBoundaryPortToBoundaryPort ( smooth , out smoothedPolyline ) ;
else
curve = RouteFromBoundaryPortToFloatingPort ( targetLoosePolyline , smooth , out smoothedPolyline ) ;
}
else {
if ( targetPort is FloatingPort ) {
ExtendVisibilityGraphFromFloatingSourcePort ( ) ;
Debug . Assert ( _sourceVisibilityVertex ! = null ) ;
//the edge has to be reversed to route from CurvePort to FloatingPort
curve = RouteFromFloatingPortToFloatingPort ( targetLoosePolyline , smooth , out smoothedPolyline ) ;
} else
curve = RouteFromFloatingPortToAnywherePort ( ( ( HookUpAnywhereFromInsidePort ) targetPort ) . LoosePolyline ,
smooth , out smoothedPolyline ,
( HookUpAnywhereFromInsidePort ) targetPort ) ;
}
return curve ;
}
ICurve RouteFromFloatingPortToAnywherePort ( Polyline targetLoosePoly , bool smooth , out SmoothedPolyline smoothedPolyline , HookUpAnywhereFromInsidePort port ) {
if ( ! port . Curve . BoundingBox . Contains ( sourcePort . Location ) ) {
smoothedPolyline = null ;
return null ;
}
_sourceVisibilityVertex = GetVertex ( sourcePort . Location ) ;
_polyline = GetShortestPolylineToMulitpleTargets ( SourceVisibilityVertex , Targets ( targetLoosePoly ) ) ;
if ( _polyline = = null ) {
smoothedPolyline = null ;
return null ;
}
if ( UseSpanner )
TryShortcutPolyline ( ) ;
RelaxPolyline ( ) ;
FixLastPolylinePointForAnywherePort ( port ) ;
if ( port . HookSize > 0 )
BuildHook ( port ) ;
return SmoothCornersAndReturnCurve ( smooth , out smoothedPolyline ) ;
}
void BuildHook ( HookUpAnywhereFromInsidePort port ) {
var curve = port . Curve ;
//creating a hook
var ellipse = new Ellipse ( port . HookSize , port . HookSize , _polyline . End ) ;
var intersections = Curve . GetAllIntersections ( curve , ellipse , true ) . ToArray ( ) ;
Debug . Assert ( intersections . Length = = 2 ) ;
if ( Point . GetTriangleOrientation ( intersections [ 0 ] . IntersectionPoint , _polyline . End , _polyline . EndPoint . Prev . Point ) = = TriangleOrientation . Counterclockwise )
intersections . Reverse ( ) ; //so the [0] point is to the left of the Polyline
var polylineTangent = ( _polyline . End - _polyline . EndPoint . Prev . Point ) . Normalize ( ) ;
var tan0 = curve . Derivative ( intersections [ 0 ] . Par0 ) . Normalize ( ) ;
var prj0 = tan0 * polylineTangent ;
if ( Math . Abs ( prj0 ) < 0.2 )
ExtendPolyline ( tan0 , intersections [ 0 ] , polylineTangent , port ) ;
else {
var tan1 = curve . Derivative ( intersections [ 1 ] . Par0 ) . Normalize ( ) ;
var prj1 = tan1 * polylineTangent ;
if ( prj1 < prj0 )
ExtendPolyline ( tan1 , intersections [ 1 ] , polylineTangent , port ) ;
else
ExtendPolyline ( tan0 , intersections [ 0 ] , polylineTangent , port ) ;
}
}
void ExtendPolyline ( Point tangentAtIntersection , IntersectionInfo x , Point polylineTangent , HookUpAnywhereFromInsidePort port ) {
var normal = tangentAtIntersection . Rotate ( Math . PI / 2 ) ;
if ( normal * polylineTangent < 0 )
normal = - normal ;
var pointBeforeLast = x . IntersectionPoint + normal * port . HookSize ;
Point pointAfterX ;
if ( ! Point . LineLineIntersection ( pointBeforeLast , pointBeforeLast + tangentAtIntersection , _polyline . End , _polyline . End + polylineTangent , out pointAfterX ) )
return ;
_polyline . AddPoint ( pointAfterX ) ;
_polyline . AddPoint ( pointBeforeLast ) ;
_polyline . AddPoint ( x . IntersectionPoint ) ;
}
void FixLastPolylinePointForAnywherePort ( HookUpAnywhereFromInsidePort port ) {
while ( true ) {
PolylinePoint lastPointInside = GetLastPointInsideOfCurveOnPolyline ( port . Curve ) ;
lastPointInside . Next . Next = null ;
_polyline . EndPoint = lastPointInside . Next ;
var dir = lastPointInside . Next . Point - lastPointInside . Point ;
dir = dir . Normalize ( ) * port . Curve . BoundingBox . Diagonal ; //make it a long vector
var dir0 = dir . Rotate ( - port . AdjustmentAngle ) ;
var dir1 = dir . Rotate ( port . AdjustmentAngle ) ;
var rx = Curve . CurveCurveIntersectionOne ( port . Curve , new LineSegment ( lastPointInside . Point , lastPointInside . Point + dir0 ) , true ) ;
var lx = Curve . CurveCurveIntersectionOne ( port . Curve , new LineSegment ( lastPointInside . Point , lastPointInside . Point + dir1 ) , true ) ;
if ( rx = = null | | lx = = null ) return ;
//this.ShowPolylineAndObstacles(Polyline, new LineSegment(lastPointInside.Point, lastPointInside.Point+dir0), new LineSegment(lastPointInside.Point, rerPoint+dir1), port.Curve);
var trimmedCurve = GetTrimmedCurveForHookingUpAnywhere ( port . Curve , lastPointInside , rx , lx ) ;
var newLastPoint = trimmedCurve [ trimmedCurve . ClosestParameter ( lastPointInside . Point ) ] ;
if ( ! LineAvoidsTightHierarchy ( new LineSegment ( lastPointInside . Point , newLastPoint ) , SourceTightPolyline , null ) ) {
var xx = Curve . CurveCurveIntersectionOne ( port . Curve , new LineSegment ( lastPointInside . Point , lastPointInside . Next . Point ) , false ) ;
if ( xx = = null ) return ;
//this.ShowPolylineAndObstacles(Polyline, port.Curve);
_polyline . EndPoint . Point = xx . IntersectionPoint ;
break ;
}
_polyline . EndPoint . Point = newLastPoint ;
if ( lastPointInside . Prev = = null | | ! TryShortcutPolyPoint ( lastPointInside . Prev ) )
break ;
}
}
static ICurve GetTrimmedCurveForHookingUpAnywhere ( ICurve curve , PolylinePoint lastPointInside , IntersectionInfo x0 , IntersectionInfo x1 ) {
var clockwise =
Point . GetTriangleOrientation ( x1 . IntersectionPoint , x0 . IntersectionPoint , lastPointInside . Point ) = =
TriangleOrientation . Clockwise ;
double rightX = x0 . Par0 ;
double leftX = x1 . Par0 ;
ICurve tr0 , tr1 ;
Curve ret ;
if ( clockwise ) {
if ( rightX < leftX )
return curve . Trim ( rightX , leftX ) ;
tr0 = curve . Trim ( rightX , curve . ParEnd ) ;
tr1 = curve . Trim ( curve . ParStart , leftX ) ;
ret = new Curve ( ) ;
return ret . AddSegs ( tr0 , tr1 ) ;
}
if ( leftX < rightX )
return curve . Trim ( leftX , rightX ) ;
tr0 = curve . Trim ( leftX , curve . ParEnd ) ;
tr1 = curve . Trim ( curve . ParStart , rightX ) ;
ret = new Curve ( ) ;
return ret . AddSegs ( tr0 , tr1 ) ;
}
PolylinePoint GetLastPointInsideOfCurveOnPolyline ( ICurve curve ) {
for ( var p = _polyline . EndPoint . Prev ; p ! = null ; p = p . Prev ) {
if ( p . Prev = = null )
return p ;
if ( Curve . PointRelativeToCurveLocation ( p . Point , curve ) = = PointLocation . Inside )
return p ;
}
throw new InvalidOperationException ( ) ;
}
Polyline GetShortestPolylineToMulitpleTargets ( VisibilityVertex sourceVisVertex , IEnumerable < VisibilityVertex > targets ) {
CleanTheGraphForShortestPath ( ) ;
//ShowPolylineAndObstacles(targets.Select(t=>new Ellipse(3,3,t.Point)).ToArray());
var pathCalc = new SingleSourceMultipleTargetsShortestPathOnVisibilityGraph ( sourceVisVertex , targets , VisibilityGraph ) ; // { dd = ShowPolylineAndObstacles };
IEnumerable < VisibilityVertex > path = pathCalc . GetPath ( ) ;
if ( path = = null )
return null ;
Debug . Assert ( path . First ( ) = = sourceVisVertex & & targets . Contains ( path . Last ( ) ) ) ;
var ret = new Polyline ( ) ;
foreach ( VisibilityVertex v in path )
ret . AddPoint ( v . Point ) ;
return RemoveCollinearVertices ( ret ) ;
}
IEnumerable < VisibilityVertex > Targets ( Polyline targetLoosePoly ) {
return new List < VisibilityVertex > ( targetLoosePoly . Select ( p = > visibilityGraph . FindVertex ( p ) ) ) ;
}
void ExtendVisibilityGraphFromFloatingSourcePort ( ) {
var fp = sourcePort as FloatingPort ;
Debug . Assert ( fp ! = null ) ;
StartPointOfEdgeRouting = fp . Location ;
if ( UseSpanner )
_sourceVisibilityVertex = AddTransientVisibilityEdgesForPort ( sourcePort . Location , SourceLoosePolyline ) ;
else {
PointVisibilityCalculator . CalculatePointVisibilityGraph (
from p in GetActivePolylines ( ) where p ! = SourceLoosePolyline select p , VisibilityGraph ,
StartPointOfEdgeRouting , VisibilityKind . Tangent , out _sourceVisibilityVertex ) ;
}
}
void FigureOutSourceTargetPolylinesAndActiveRectangle ( ) {
_sourceTightPolyline = GetFirstHitPolyline ( sourcePort . Location , ObstacleCalculator . RootOfTightHierarchy ) ;
SourceLoosePolyline = GetFirstHitPolyline ( sourcePort . Location , ObstacleCalculator . RootOfLooseHierarchy ) ;
targetTightPolyline = GetFirstHitPolyline ( targetPort . Location , ObstacleCalculator . RootOfTightHierarchy ) ;
targetLoosePolyline = GetFirstHitPolyline ( targetPort . Location , ObstacleCalculator . RootOfLooseHierarchy ) ;
activeRectangle = new Rectangle ( new Point ( double . NegativeInfinity , double . PositiveInfinity ) ,
new Point ( double . PositiveInfinity , double . NegativeInfinity ) ) ;
}
IEnumerable < Polygon > AllPolygons ( ) {
foreach ( Polyline p in ObstacleCalculator . LooseObstacles )
yield return new Polygon ( p ) ;
}
/// <summary>
/// </summary>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
public VisibilityGraph GetVisibilityGraph ( ) {
return VisibilityGraph ;
}
// internal void CalculateVisibilityGraph(IEnumerable<EdgeGeometry> edgeGeometries, bool qualityAtPorts)
// {
// CalculateWholeTangentVisibilityGraph();
// if (ConeSpannerAngle > 0 && qualityAtPorts && edgeGeometries != null)
// CalculatePortVisibilityGraph(GetPortLocationsPointSet(edgeGeometries));
// }
#if DEBUG_MSAGL & & ! SILVERLIGHT
internal void ShowObstaclesAndVisGraph ( )
{
var obs = this . obstacleCalculator . LooseObstacles . Select ( o = > new DebugCurve ( 100 , 1 , "blue" , o ) ) ;
var edges =
visibilityGraph . Edges . Select (
e = >
new DebugCurve ( 70 , 1 , ( e is TransientVisibilityEdge ? "red" : "green" ) ,
new LineSegment ( e . SourcePoint , e . TargetPoint ) ) ) ;
LayoutAlgorithmSettings . ShowDebugCurvesEnumeration ( obs . Concat ( edges ) ) ;
}
#endif
internal void AddActivePolygons ( IEnumerable < Polygon > polygons ) {
activePolygons . AddRange ( polygons ) ;
}
internal void ClearActivePolygons ( ) {
activePolygons . Clear ( ) ;
}
}
}