using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using Microsoft.Msagl.Core.Geometry;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
using Microsoft.Msagl.Core;
namespace Microsoft.Msagl.Routing.Rectilinear{
///
///
public static class RectilinearInteractiveEditor {
///
/// Create a RectilinearEdgeRouter from the passed obstacleNodes, with one port at the center of each obstacle,
/// and route between the obstacles.
///
/// The radius of the arc inscribed into path corners
/// The minimum padding from an obstacle's curve to its enclosing polyline
/// The nodes of the graph
/// Edges defining the nodes to route between, and receiving the resultant paths
/// Mode of the edges (Rectilinear or RectilinearToCenter).
/// Use a more memory-efficient but possibly path-suboptimal visibility graph
/// Use obstacle bounding box rectangles in visibility graph
/// The cost penalty for a bend in the path, as a percentage of the Manhattan distance
/// between the source target ports.
static public void CreatePortsAndRouteEdges(double cornerFitRadius, double padding
, IEnumerable obstacleNodes, IEnumerable geometryEdges
, EdgeRoutingMode edgeRoutingMode, bool useSparseVisibilityGraph
, bool useObstacleRectangles, double bendPenaltyAsAPercentageOfDistance) {
var r = FillRouter(cornerFitRadius, padding, obstacleNodes, geometryEdges, edgeRoutingMode, useSparseVisibilityGraph
, useObstacleRectangles, bendPenaltyAsAPercentageOfDistance);
r.Run();
CreateSelfEdges(geometryEdges.Where(e => e.SourcePort.Location == e.TargetPort.Location), cornerFitRadius);
}
///
/// Create a RectilinearEdgeRouter from the passed obstacleNodes, with one port at the center of each obstacle,
/// and route between the obstacles, with default bend penalty.
///
/// The radius of the arc inscribed into path corners
/// The minimum padding from an obstacle's curve to its enclosing polyline
/// The nodes of the graph
/// Edges defining the nodes to route between, and receiving the resultant paths
/// Mode of the edges (Rectilinear or RectilinearToCenter).
/// Use obstacle bounding box rectangles in visibility graph
/// Use a more memory-efficient but possibly path-suboptimal visibility graph
static public void CreatePortsAndRouteEdges(double cornerFitRadius, double padding
, IEnumerable obstacleNodes, IEnumerable geometryEdges
, EdgeRoutingMode edgeRoutingMode, bool useSparseVisibilityGraph
, bool useObstacleRectangles) {
CreatePortsAndRouteEdges(cornerFitRadius, padding, obstacleNodes, geometryEdges, edgeRoutingMode
, useSparseVisibilityGraph, useObstacleRectangles, SsstRectilinearPath.DefaultBendPenaltyAsAPercentageOfDistance);
}
///
/// Create a RectilinearEdgeRouter from the passed obstacleNodes, with one port at the center of each obstacle,
/// and route between the obstacles, with default bend penalty.
///
/// The radius of the arc inscribed into path corners
/// The minimum padding from an obstacle's curve to its enclosing polyline
/// The nodes of the graph
/// Edges defining the nodes to route between, and receiving the resultant paths
/// Mode of the edges (Rectilinear or RectilinearToCenter).
/// Use a more memory-efficient but possibly path-suboptimal visibility graph
static public void CreatePortsAndRouteEdges(double cornerFitRadius, double padding
, IEnumerable obstacleNodes, IEnumerable geometryEdges
, EdgeRoutingMode edgeRoutingMode, bool useSparseVisibilityGraph)
{
CreatePortsAndRouteEdges(cornerFitRadius, padding, obstacleNodes, geometryEdges, edgeRoutingMode
, useSparseVisibilityGraph, false, SsstRectilinearPath.DefaultBendPenaltyAsAPercentageOfDistance);
}
///
/// Create a RectilinearEdgeRouter populated with the passed obstacles.
///
/// The populated RectilinearEdgeRouter
static RectilinearEdgeRouter FillRouter(double cornerFitRadius, double padding, IEnumerable obstacleNodes, IEnumerable geomEdges
, EdgeRoutingMode edgeRoutingMode, bool useSparseVisibilityGraph, bool useObstacleRectangles, double bendPenaltyAsAPercentageOfDistance) {
Debug.Assert((EdgeRoutingMode.Rectilinear == edgeRoutingMode) || (EdgeRoutingMode.RectilinearToCenter == edgeRoutingMode)
, "Non-rectilinear edgeRoutingMode");
var nodeShapeMap = new Dictionary();
foreach (Node node in obstacleNodes)
{
Shape shape = CreateShapeWithRelativeNodeAtCenter(node);
nodeShapeMap.Add(node, shape);
}
var router = new RectilinearEdgeRouter(nodeShapeMap.Values, padding, cornerFitRadius, useSparseVisibilityGraph, useObstacleRectangles)
{
RouteToCenterOfObstacles = (edgeRoutingMode == EdgeRoutingMode.RectilinearToCenter),
BendPenaltyAsAPercentageOfDistance = bendPenaltyAsAPercentageOfDistance
};
foreach (var geomEdge in geomEdges) {
var edgeGeom = geomEdge.EdgeGeometry;
edgeGeom.SourcePort = nodeShapeMap[geomEdge.Source].Ports.First();
edgeGeom.TargetPort = nodeShapeMap[geomEdge.Target].Ports.First();
router.AddEdgeGeometryToRoute(edgeGeom);
}
return router;
}
static void CreateSelfEdges(IEnumerable selfEdges, double cornerFitRadius) {
foreach (var edge in selfEdges) {
CreateSimpleEdgeCurveWithGivenFitRadius(edge, cornerFitRadius);
}
}
///
///
///
///
///
public static void CreateSimpleEdgeCurveWithGivenFitRadius(Edge edge, double cornerFitRadius)
{
ValidateArg.IsNotNull(edge, "edge");
var a = edge.Source.Center;
var b = edge.Target.Center;
if (edge.Source == edge.Target)
{
var dx = edge.Source.BoundaryCurve.BoundingBox.Width / 2;
var dy = edge.Source.BoundingBox.Height / 4;
edge.UnderlyingPolyline = CreateUnderlyingPolylineForSelfEdge(a, dx, dy);
for (var site = edge.UnderlyingPolyline.HeadSite.Next; site.Next != null; site = site.Next) CalculateCoefficiensUnderSite(site, cornerFitRadius);
edge.Curve = edge.UnderlyingPolyline.CreateCurve();
}
else
{
edge.UnderlyingPolyline = SmoothedPolyline.FromPoints(new[] { a, b });
edge.Curve = edge.UnderlyingPolyline.CreateCurve();
}
if (!Arrowheads.TrimSplineAndCalculateArrowheads(edge.EdgeGeometry, edge.Source.BoundaryCurve, edge.Target.BoundaryCurve, edge.Curve,
true, false))
Arrowheads.CreateBigEnoughSpline(edge);
}
///
/// creates an edge curve based only on the source and target geometry
///
///
public static void CreateSimpleEdgeCurve(Edge edge)
{
ValidateArg.IsNotNull(edge, "edge");
var a = edge.Source.Center;
var b = edge.Target.Center;
if (edge.Source == edge.Target)
{
var dx = edge.Source.BoundaryCurve.BoundingBox.Width / 2;
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();
}
if (!Arrowheads.TrimSplineAndCalculateArrowheads(edge.EdgeGeometry, edge.Source.BoundaryCurve, edge.Target.BoundaryCurve, edge.Curve, true, false))
Arrowheads.CreateBigEnoughSpline(edge);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Msagl.Core.Geometry.Site")]
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;
}
///
/// Create a Shape with a single relative port at its center.
///
/// The node from which the shape is derived
///
public static Shape CreateShapeWithRelativeNodeAtCenter(Node node) {
ValidateArg.IsNotNull(node, "node");
var shape = new RelativeShape(() => node.BoundaryCurve);
shape.Ports.Insert(new RelativeFloatingPort(() => node.BoundaryCurve, () => node.Center));
return shape;
}
private static void CalculateCoefficiensUnderSite(Site site, double radius){
double l = radius/(site.Point - site.Previous.Point).Length;
l = Math.Min(0.5, l);
site.PreviousBezierSegmentFitCoefficient = l;
l = radius / (site.Next.Point-site.Point).Length;
l = Math.Min(0.5, l);
site.NextBezierSegmentFitCoefficient = l;
}
}
}