using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Msagl.Core;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Layout;
namespace Microsoft.Msagl.Routing
{
///
/// Basic edge router for producing straight edges.
///
public class StraightLineEdges : AlgorithmBase
{
private readonly IEnumerable edges;
private readonly double padding;
///
/// Constructs a basic straight edge router.
///
public StraightLineEdges(IEnumerable edges, double padding)
{
this.edges = edges;
this.padding = padding;
}
///
/// Executes the algorithm.
///
protected override void RunInternal()
{
this.StartListenToLocalProgress(edges.Count());
SplineRouter.CreatePortsIfNeeded(edges);
foreach (Edge edge in edges)
{
RouteEdge(edge, this.padding);
this.ProgressStep();
}
}
///
/// populate the geometry including curve and arrowhead positioning for the given edge using simple
/// straight line routing style. Self edges will be drawn as a loop, padding is used to control the
/// size of the loop.
///
/// edge to route
/// controls size of loop
public static void RouteEdge(Edge edge, double padding)
{
ValidateArg.IsNotNull(edge, "edge");
var eg = edge.EdgeGeometry;
if (eg.SourcePort == null)
{
#if SHARPKIT // Lambdas bind differently in JS
eg.SourcePort = ((Func)(ed => new RelativeFloatingPort(() => ed.Source.BoundaryCurve,
() => ed.Source.Center)))(edge);
#else
eg.SourcePort = new RelativeFloatingPort(() => edge.Source.BoundaryCurve, () => edge.Source.Center);
#endif
}
if (eg.TargetPort == null)
{
#if SHARPKIT // Lambdas bind differently in JS
eg.TargetPort = ((Func)(ed => new RelativeFloatingPort(() => ed.Target.BoundaryCurve,
() => ed.Target.Center)))(edge);
#else
eg.TargetPort = new RelativeFloatingPort(() => edge.Target.BoundaryCurve, () => edge.Target.Center);
#endif
}
if (!ContainmentLoop(eg, padding))
{
eg.Curve = GetEdgeLine(edge);
}
Arrowheads.TrimSplineAndCalculateArrowheads(eg, eg.SourcePort.Curve,
eg.TargetPort.Curve, edge.Curve, false, false);
}
internal static bool ContainmentLoop(EdgeGeometry eg, double padding) {
var sourceCurve = eg.SourcePort.Curve;
var targetCurve = eg.TargetPort.Curve;
if (sourceCurve == null || targetCurve == null)
return false;
Rectangle targetBox = sourceCurve.BoundingBox;
Rectangle sourceBox = targetCurve.BoundingBox;
bool targetInSource = targetBox.Contains(sourceBox);
bool sourceInTarget = (!targetInSource) && sourceBox.Contains(targetBox);
if (targetInSource || sourceInTarget) {
eg.Curve = CreateLoop(targetBox, sourceBox, sourceInTarget, padding);
return true;
}
return false;
}
private static Curve CreateLoop(Rectangle targetBox, Rectangle sourceBox, bool sourceContainsTarget, double padding)
{
return sourceContainsTarget ? CreateLoop(targetBox, sourceBox, padding, false) : CreateLoop(sourceBox, targetBox, padding, true);
}
///
/// creates a loop from sourceBox center to the closest point on the targetBox boundary
///
///
/// contains sourceBox
///
/// reverse the loop if true
///
internal static Curve CreateLoop(Rectangle sourceBox, Rectangle targetBox, double howMuchToStickOut, bool reverse)
{
var center=sourceBox.Center;
var closestPoint = FindClosestPointOnBoxBoundary(sourceBox.Center, targetBox);
var dir = closestPoint - center;
var vert=Math.Abs(dir.X)
/// Returns a line segment for the given edge.
///
/// The line segment representing the given edge.
public static LineSegment GetEdgeLine(Edge edge)
{
ValidateArg.IsNotNull(edge, "edge");
Point sourcePoint;
ICurve sourceBox;
if (edge.SourcePort == null)
{
sourcePoint = edge.Source.Center;
sourceBox = edge.Source.BoundaryCurve;
}
else
{
sourcePoint = edge.SourcePort.Location;
sourceBox = edge.SourcePort.Curve;
}
Point targetPoint;
ICurve targetBox;
if (edge.TargetPort == null)
{
targetPoint = edge.Target.Center;
targetBox = edge.Target.BoundaryCurve;
}
else
{
targetPoint = edge.TargetPort.Location;
targetBox = edge.TargetPort.Curve;
}
LineSegment line = new LineSegment(sourcePoint, targetPoint);
IList intersects = Curve.GetAllIntersections(sourceBox, line, false);
if (intersects.Count > 0)
{
var trimmedLine = (LineSegment)line.Trim(intersects[0].Par1, 1.0);
if(trimmedLine != null)
{
line = trimmedLine;
intersects = Curve.GetAllIntersections(targetBox, line, false);
if (intersects.Count > 0)
{
trimmedLine = (LineSegment)line.Trim(0.0, intersects[0].Par1);
if(trimmedLine != null)
{
line = trimmedLine;
}
}
}
}
return line;
}
///
/// creates an edge curve based only on the source and target geometry
///
///
public static void CreateSimpleEdgeCurveWithUnderlyingPolyline(Edge edge)
{
ValidateArg.IsNotNull(edge, "edge");
var a = edge.Source.Center;
var b = edge.Target.Center;
if (edge.Source == edge.Target)
{
var dx = 2.0 / 3 * edge.Source.BoundaryCurve.BoundingBox.Width;
var dy = edge.Source.BoundingBox.Height / 4;
edge.UnderlyingPolyline = CreateUnderlyingPolylineForSelfEdge(a, dx, dy);
edge.Curve = edge.UnderlyingPolyline.CreateCurve();
}
else
{
edge.UnderlyingPolyline = SmoothedPolyline.FromPoints(new[] { a, b });
edge.Curve = edge.UnderlyingPolyline.CreateCurve();
}
Arrowheads.TrimSplineAndCalculateArrowheads(edge.EdgeGeometry,
edge.Source.BoundaryCurve,
edge.Target.BoundaryCurve,
edge.Curve, false, false);
}
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Msagl.Core.Geometry.Site")]
internal static SmoothedPolyline CreateUnderlyingPolylineForSelfEdge(Point p0, double dx, double dy)
{
var p1 = p0 + new Point(0, dy);
var p2 = p0 + new Point(dx, dy);
var p3 = p0 + new Point(dx, -dy);
var p4 = p0 + new Point(0, -dy);
var site = new Site(p0);
var polyline = new SmoothedPolyline(site);
site = new Site(site, p1);
site = new Site(site, p2);
site = new Site(site, p3);
site = new Site(site, p4);
new Site(site, p0);
return polyline;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Msagl.Site")]
static internal void SetStraightLineEdgesWithUnderlyingPolylines(GeometryGraph graph) {
SplineRouter.CreatePortsIfNeeded(graph.Edges);
foreach (Edge edge in graph.Edges)
CreateSimpleEdgeCurveWithUnderlyingPolyline(edge);
}
}
}