using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using Microsoft.Msagl.Core.Layout; using Microsoft.Msagl.Core.Routing; using Microsoft.Msagl.Routing.Spline.Bundling; using Microsoft.Msagl.Core.GraphAlgorithms; namespace Microsoft.Msagl.Routing { internal class MultiEdgeRouter { readonly List multiEdgeGeometries; readonly InteractiveEdgeRouter interactiveEdgeRouter; readonly BundlingSettings bundlingSettings; readonly Func> transparentShapeSetter; readonly RectangleNode nodeTree; internal MultiEdgeRouter(List multiEdgeGeoms, InteractiveEdgeRouter interactiveEdgeRouter, IEnumerable nodeBoundaryCurves, BundlingSettings bundlingSettings, Func> transparentShapeSetter) { multiEdgeGeometries = multiEdgeGeoms.Select(l => l.Select(e => e.EdgeGeometry).ToArray()).ToList(); this.interactiveEdgeRouter = interactiveEdgeRouter; this.bundlingSettings = bundlingSettings; this.transparentShapeSetter = transparentShapeSetter; nodeTree = RectangleNode.CreateRectangleNodeOnData(nodeBoundaryCurves, c => c.BoundingBox); } internal void Run() { foreach (GeometryGraph graph in GetGeometryGraphs()) { var br = new BundleRouter(graph, new SdShortestPath(transparentShapeSetter, null, null), interactiveEdgeRouter.VisibilityGraph, bundlingSettings, interactiveEdgeRouter.LoosePadding, interactiveEdgeRouter.TightHierarchy, interactiveEdgeRouter.LooseHierarchy, null, null, null); br.Run(); } } IEnumerable GetGeometryGraphs() { foreach (PreGraph preGraph in GetIndependantPreGraphs()) yield return CreateGeometryGraph(preGraph); } GeometryGraph CreateGeometryGraph(PreGraph preGraph) { var graph = new GeometryGraph(); var nodeDictionary = new Dictionary(); foreach (var curve in preGraph.nodeBoundaries) { var node = new Node(curve); nodeDictionary[curve] = node; graph.Nodes.Add(node); } foreach (var eg in preGraph.edgeGeometries) AddEdgeGeometryToGraph(eg, graph, nodeDictionary); return graph; } void AddEdgeGeometryToGraph(EdgeGeometry eg, GeometryGraph graph, Dictionary nodeDictionary) { var sourceNode = GetOrCreateNode(eg.SourcePort, nodeDictionary); var targetNode = GetOrCreateNode(eg.TargetPort, nodeDictionary); var edge = new Edge(sourceNode, targetNode) { EdgeGeometry = eg }; graph.Edges.Add(edge); } private Node GetOrCreateNode(Port port, Dictionary nodeDictionary) { var curve = GetPortCurve(port); Node node; if (!nodeDictionary.TryGetValue(curve, out node)) nodeDictionary[curve] = node = new Node(curve); return node; } private ICurve GetPortCurve(Port port) { var curve = nodeTree.FirstHitNode(port.Location, (point, c) => Curve.PointRelativeToCurveLocation(point, c) != PointLocation.Outside ? HitTestBehavior.Stop : HitTestBehavior.Continue).UserData; return curve; } /// /// creates a set of pregraphs suitable for bundle routing /// /// IEnumerable GetIndependantPreGraphs() { List preGraphs = CreateInitialPregraphs(); do { int count = preGraphs.Count; UniteConnectedPreGraphs(ref preGraphs); if (count <= preGraphs.Count) break; } while (true); return preGraphs; } void UniteConnectedPreGraphs(ref List preGraphs) { BasicGraph intersectionGraph = GetIntersectionGraphOfPreGraphs(preGraphs); if (intersectionGraph == null) return; var connectedComponents = ConnectedComponentCalculator.GetComponents(intersectionGraph); var newPreGraphList = new List(); foreach (var component in connectedComponents) { PreGraph preGraph = null; foreach (var i in component) { if (preGraph == null) { preGraph = preGraphs[i]; newPreGraphList.Add(preGraph); } else preGraph.AddGraph(preGraphs[i]); } } preGraphs = newPreGraphList; foreach (var pg in preGraphs) AddIntersectingNodes(pg); } private void AddIntersectingNodes(PreGraph pg) { var rect = pg.boundingBox; foreach (var curve in nodeTree.GetNodeItemsIntersectingRectangle(rect)) pg.AddNodeBoundary(curve); } static BasicGraph GetIntersectionGraphOfPreGraphs(List preGraphs) { var intersectingPairs = EnumeratePairsOfIntersectedPreGraphs(preGraphs); if (intersectingPairs.Any()) return new BasicGraph(intersectingPairs, preGraphs.Count); return null; } static IEnumerable EnumeratePairsOfIntersectedPreGraphs(List preGraphs) { var rn = RectangleNode.CreateRectangleNodeOnData(Enumerable.Range(0, preGraphs.Count), i => preGraphs[i].boundingBox); var list = new List(); RectangleNodeUtils.CrossRectangleNodes(rn, rn, (a, b) => list.Add(new IntPair(a, b))); return list; } List CreateInitialPregraphs() { return new List(multiEdgeGeometries.Select(CreatePregraphFromSetOfEdgeGeometries)); } private PreGraph CreatePregraphFromSetOfEdgeGeometries(EdgeGeometry[] egs) { var nodeBoundaries = new Set(); var eg = egs[0]; var c = GetPortCurve(eg.SourcePort); #if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=369 there are no structs in js var rect = c.BoundingBox.Clone(); #else var rect = c.BoundingBox; #endif nodeBoundaries.Insert(c); nodeBoundaries.Insert(eg.TargetPort.Curve); rect.Add(eg.TargetPort.Curve.BoundingBox); var overlapped = nodeTree.GetNodeItemsIntersectingRectangle(rect); foreach (var nodeBoundary in overlapped) nodeBoundaries.Insert(nodeBoundary); return new PreGraph(egs, nodeBoundaries); } } }