/// <summary> /// Calculates the graph layout /// </summary> /// <exception cref="CancelException">Thrown when the layout is canceled.</exception> #else /// <summary> /// Calculates the graph layout /// </summary> /// <exception cref="System.OperationCanceledException">Thrown when the layout is canceled.</exception> #endif public static void CalculateLayout(GeometryGraph geometryGraph, LayoutAlgorithmSettings settings, CancelToken cancelToken) { Console.WriteLine("starting CalculateLayout"); if (settings is RankingLayoutSettings) { var rankingLayoutSettings = settings as RankingLayoutSettings; var rankingLayout = new RankingLayout(rankingLayoutSettings, geometryGraph); rankingLayout.Run(cancelToken); RouteAndLabelEdges(geometryGraph, settings, geometryGraph.Edges); } else if (settings is MdsLayoutSettings) { var mdsLayoutSettings = settings as MdsLayoutSettings; var mdsLayout = new MdsGraphLayout(mdsLayoutSettings, geometryGraph); mdsLayout.Run(cancelToken); if (settings.EdgeRoutingSettings.EdgeRoutingMode != EdgeRoutingMode.None) RouteAndLabelEdges(geometryGraph, settings, geometryGraph.Edges); } else if (settings is FastIncrementalLayoutSettings) { var incrementalSettings = settings as FastIncrementalLayoutSettings; incrementalSettings.AvoidOverlaps = true; var initialLayout = new InitialLayout(geometryGraph, incrementalSettings); initialLayout.Run(cancelToken); if (settings.EdgeRoutingSettings.EdgeRoutingMode != EdgeRoutingMode.None) RouteAndLabelEdges(geometryGraph, settings, geometryGraph.Edges); //incrementalSettings.IncrementalRun(geometryGraph); } else { var sugiyamaLayoutSettings = settings as SugiyamaLayoutSettings; if (sugiyamaLayoutSettings != null) ProcessSugiamaLayout(geometryGraph, sugiyamaLayoutSettings, cancelToken); else { Debug.Assert(settings is LgLayoutSettings); LayoutLargeGraphWithLayers(geometryGraph, settings, cancelToken); } } }
/// <summary> /// Constructor /// </summary> /// <param name="streamPar">the stream to write the graph into</param> /// <param name="graphP">the graph</param> /// <param name="settings">The settings to be written.</param> public GeometryGraphWriter(Stream streamPar, GeometryGraph graphP, LayoutAlgorithmSettings settings) { stream = streamPar; Graph = graphP; Settings = settings; var xmlWriterSettings = new XmlWriterSettings {Indent = true}; XmlWriter = XmlWriter.Create(stream, xmlWriterSettings); EdgeEnumeration = graphP.Edges; }
internal static void FixBoundingBox(GeometryGraph component, LayoutAlgorithmSettings settings) { // Pad the graph with margins so the packing will be spaced out. component.Margins = settings.ClusterMargin; component.UpdateBoundingBox(); // Zero the graph component.Translate(-component.BoundingBox.LeftBottom); }
/// <summary> /// /// </summary> /// <param name="pushingNodes">the nodes are already at the correct positions</param> /// <param name="graph"></param> /// <param name="layoutSettings"></param> public IncrementalDragger(IEnumerable<GeomNode> pushingNodes, GeometryGraph graph, LayoutAlgorithmSettings layoutSettings) { this.graph = graph; this.nodeSeparation = layoutSettings.NodeSeparation; this.layoutSettings = layoutSettings; pushingNodesArray = pushingNodes as GeomNode[] ?? pushingNodes.ToArray(); Debug.Assert(pushingNodesArray.All(n => DefaultClusterParent(n) == null) || (new Set<GeomNode>(pushingNodesArray.Select(n => n.ClusterParents.First()))).Count == 1, "dragged nodes have to belong to the same cluster"); InitBumperPushers(); }
internal static void RouteEdges(GeometryGraph component, LayoutAlgorithmSettings settings, CancelToken cancelToken) { EdgeRoutingMode mode = settings.EdgeRoutingSettings.EdgeRoutingMode; // Use straight line routing on very large graphs otherwise it is too slow if (component.Nodes.Count >= 2000) { mode = EdgeRoutingMode.StraightLine; } switch (mode) { case EdgeRoutingMode.Spline: var splineRouter = new SplineRouter( component, settings.EdgeRoutingSettings.Padding, settings.NodeSeparation, settings.EdgeRoutingSettings.ConeAngle, null); splineRouter.Run(cancelToken); break; case EdgeRoutingMode.SplineBundling: splineRouter = new SplineRouter( component, settings.EdgeRoutingSettings.Padding, settings.NodeSeparation / 20, settings.EdgeRoutingSettings.ConeAngle, new BundlingSettings()); splineRouter.Run(cancelToken); break; case EdgeRoutingMode.Rectilinear: double edgePadding = settings.EdgeRoutingSettings.Padding; double cornerRadius = settings.EdgeRoutingSettings.CornerRadius; var rectilinearEdgeRouter = new RectilinearEdgeRouter(component, edgePadding, cornerRadius, true); rectilinearEdgeRouter.Run(cancelToken); break; case EdgeRoutingMode.StraightLine: var router = new StraightLineEdges(component.Edges, settings.EdgeRoutingSettings.Padding); router.Run(cancelToken); break; } }
static void ProcessGraphAttrs(Graph graph, GeometryGraph msaglGraph, LayoutAlgorithmSettings settings) { msaglGraph.Margins = graph.Attr.Margin; var ss = settings as SugiyamaLayoutSettings; if (ss != null) { switch (graph.Attr.LayerDirection) { case LayerDirection.None: case LayerDirection.TB: break; case LayerDirection.LR: ss.Transformation = PlaneTransformation.Rotation(Math.PI/2); break; case LayerDirection.RL: ss.Transformation = PlaneTransformation.Rotation(-Math.PI/2); break; case LayerDirection.BT: ss.Transformation = PlaneTransformation.Rotation(Math.PI); break; default: throw new InvalidOperationException(); //"unexpected layout direction"); } TransferConstraints(ss, graph); } }
public static void Write(GeometryGraph graph, LayoutAlgorithmSettings settings, string fileName) { if (fileName == null) return; if (!fileName.EndsWith(FileExtension, StringComparison.InvariantCultureIgnoreCase)) fileName += FileExtension; using (Stream stream = File.Open(fileName, FileMode.Create)) { var graphWriter = new GeometryGraphWriter(stream, graph, settings); graphWriter.Write(); } }
internal void LayoutComponent(LayoutAlgorithmSettings settings, GeometryGraph component) { var fdSettings = settings as FastIncrementalLayoutSettings; var mdsSettings = settings as MdsLayoutSettings; var layeredSettings = settings as SugiyamaLayoutSettings; if (fdSettings != null) { ForceDirectedLayout(fdSettings, component); } else if (mdsSettings != null) { MDSLayout(mdsSettings, component); } else if (layeredSettings != null) { LayeredLayout(layeredSettings, component); } else { throw new NotImplementedException("Unknown type of layout settings!"); } //LayoutAlgorithmSettings.ShowGraph(component); }
/// <summary> /// Recursively lay out the clusters of the given graph using the given settings. /// </summary> /// <param name="graph">The graph being operated on.</param> /// <param name="defaultSettings">Settings to use if none is provided for a particular cluster or its ancestors.</param> public InitialLayoutByCluster(GeometryGraph graph, LayoutAlgorithmSettings defaultSettings) : this(graph, anyCluster => defaultSettings) {}
static public void ComputeNodeLabelsOfLargeGraphWithLayers(GeometryGraph geometryGraph, LayoutAlgorithmSettings settings, List<double> noldeLabelRatios, CancelToken cancelToken) { var largeGraphLayoutSettings = (LgLayoutSettings)settings; var largeGraphLayout = largeGraphLayoutSettings.Interactor; largeGraphLayout.InitNodeLabelWidthToHeightRatios(noldeLabelRatios); largeGraphLayout.LabelingOfOneRun(); }
/// <summary> /// calculates all data necessery for large graph browsing /// </summary> /// <param name="geometryGraph"></param> /// <param name="settings"></param> /// <param name="cancelToken"></param> static public void LayoutLargeGraphWithLayers(GeometryGraph geometryGraph, LayoutAlgorithmSettings settings, CancelToken cancelToken) { var largeGraphLayoutSettings = (LgLayoutSettings) settings; var largeGraphLayout = new LgInteractor(geometryGraph, largeGraphLayoutSettings, cancelToken); largeGraphLayoutSettings.Interactor = largeGraphLayout; largeGraphLayout.Run(); }
void BindTheGraphs(Graph drawingGraph, GeometryGraph geomGraph, LayoutAlgorithmSettings settings) { drawingGraph.GeometryGraph = geomGraph; foreach (Node dn in drawingGraph.NodeMap.Values) { var geomNode = dn.GeometryNode = geometryGraphReader.FindNodeById(dn.Id); geomNode.UserData = dn; } foreach (var subgraph in drawingGraph.RootSubgraph.AllSubgraphsDepthFirst()) { var geomNode = subgraph.GeometryNode = geometryGraphReader.FindClusterById(subgraph.Id); if (geomNode != null) geomNode.UserData = subgraph; } // geom edges have to appear in the same order as drawing edges for(int i = 0;i < EdgeList.Count;i++) { var drawingEdge = EdgeList[i]; var geomEdge = geometryGraphReader.EdgeList[i]; drawingEdge.GeometryEdge = geomEdge; geomEdge.UserData = drawingEdge; if(drawingEdge.Label != null) { drawingEdge.Label.GeometryLabel = geomEdge.Label; geomEdge.Label.UserData = drawingEdge.Label; } } drawingGraph.LayoutAlgorithmSettings = settings; }
/// <summary> /// </summary> /// <param name="geometryGraph"></param> /// <param name="layoutSettings"></param> /// <param name="edgesToRoute"></param> public static void RouteAndLabelEdges(GeometryGraph geometryGraph, LayoutAlgorithmSettings layoutSettings, IEnumerable<Edge> edgesToRoute) { //todo: what about parent edges!!!! var filteredEdgesToRoute = edgesToRoute.Where(e => ! e.UnderCollapsedCluster()).ToArray(); var ers = layoutSettings.EdgeRoutingSettings; if (ers.EdgeRoutingMode == EdgeRoutingMode.Rectilinear || ers.EdgeRoutingMode == EdgeRoutingMode.RectilinearToCenter) { RectilinearInteractiveEditor.CreatePortsAndRouteEdges( layoutSettings.NodeSeparation/3, layoutSettings.NodeSeparation/3, geometryGraph.Nodes, edgesToRoute, ers.EdgeRoutingMode, true, ers.UseObstacleRectangles, ers.BendPenalty); } else if (ers.EdgeRoutingMode == EdgeRoutingMode.Spline || ers.EdgeRoutingMode==EdgeRoutingMode.SugiyamaSplines) { new SplineRouter(geometryGraph, filteredEdgesToRoute, ers.Padding, ers.PolylinePadding, ers.ConeAngle, null) { ContinueOnOverlaps = true, KeepOriginalSpline = ers.KeepOriginalSpline }.Run(); } else if (ers.EdgeRoutingMode == EdgeRoutingMode.SplineBundling) { var edgeBundlingSettings = ers.BundlingSettings ?? new BundlingSettings(); var bundleRouter = new SplineRouter(geometryGraph, filteredEdgesToRoute, ers.Padding, ers.PolylinePadding, ers.ConeAngle, edgeBundlingSettings) { KeepOriginalSpline = ers.KeepOriginalSpline }; bundleRouter.Run(); if (bundleRouter.OverlapsDetected) { new SplineRouter(geometryGraph, filteredEdgesToRoute, ers.Padding, ers.PolylinePadding, ers.ConeAngle, null) { ContinueOnOverlaps = true, KeepOriginalSpline = ers.KeepOriginalSpline }.Run(); } } else if (ers.EdgeRoutingMode == EdgeRoutingMode.StraightLine) { var router = new StraightLineEdges(filteredEdgesToRoute, ers.Padding); router.Run(); } var elb = new EdgeLabelPlacement(geometryGraph.Nodes, filteredEdgesToRoute); elb.Run(); geometryGraph.UpdateBoundingBox(); }
static GeometryGraph GetTestGraphWithClusters(out LayoutAlgorithmSettings settings) { GeometryGraph graph = GeometryGraphReader.CreateFromFile( "C:\\dev\\GraphLayout\\MSAGLTests\\Resources\\MSAGLGeometryGraphs\\abstract.msagl.geom", //"E:\\dev\\MSAGL\\GraphLayout\\MSAGLTests\\Resources\\MSAGLGeometryGraphs\\abstract.msagl.geom", out settings); foreach (var edge in graph.Edges) { edge.Curve = null; edge.EdgeGeometry.TargetArrowhead = null; } graph.UpdateBoundingBox(); var root = graph.RootCluster; var a = new Cluster {UserData = "a"}; foreach (string id in new[] {"17", "39", "13", "19", "28", "12"}) a.AddChild(graph.FindNodeByUserData(id)); var b = new Cluster {UserData = "b"}; b.AddChild(a); b.AddChild(graph.FindNodeByUserData("18")); root.AddChild(b); var c = new Cluster {UserData = "c"}; foreach (string id in new[] {"30", "5", "6", "7", "8"}) c.AddChild(graph.FindNodeByUserData(id)); root.AddChild(c); var clusterNodes = new Set<Node>(root.AllClustersDepthFirst().SelectMany(cl => cl.Nodes)); foreach (var node in graph.Nodes.Where(n => clusterNodes.Contains(n) == false)) root.AddChild(node); FixClusterBoundariesWithNoRectBoundaries(root, 5); var fastIncrementalLayoutSettings = new FastIncrementalLayoutSettings(); var d=new Dictionary<Cluster, LayoutAlgorithmSettings>(); d[root] = new FastIncrementalLayoutSettings { AvoidOverlaps = true }; var initialLayout = new InitialLayoutByCluster(graph, fastIncrementalLayoutSettings); initialLayout.Run(); graph.UpdateBoundingBox(); //FixClusterBoundariesWithNoRectBoundaries(root, 5); return graph; }
void ReadGraph() { MoveToContent(); _graph.Margins = GetDoubleAttributeOrDefault(GeometryToken.Margins, 10); if (XmlReader.Name.ToLower() != GeometryToken.Graph.ToString().ToLower()) Error("expecting element \"graph\""); bool done = false; do { switch (GetElementTag()) { case GeometryToken.Nodes: ReadNodes(); break; case GeometryToken.Edges: ReadEdges(); break; case GeometryToken.Clusters: ReadClusters(); break; case GeometryToken.LayoutAlgorithmSettings: Settings = ReadLayoutAlgorithmSettings(XmlReader); //todo . not tested break; case GeometryToken.LgLevels: ReadLgLevels(); break; case GeometryToken.LgSkeletonLevels: ReadLgSkeletonLevels(); break; case GeometryToken.End: case GeometryToken.Graph: if (XmlReader.NodeType == XmlNodeType.EndElement) { done = true; ReadEndElement(); break; } XmlRead(); break; default: //ignore this element XmlReader.Skip(); break; // XmlReader.Skip(); // ReadHeader(); // if (TokenIs(GeometryToken.LayoutAlgorithmSettings)) // this.Settings = ReadLayoutAlgorithmSettings(XmlReader); // ReadNodes(); // ReadClusters(); // ReadEdges(); } } while (!done); _graph.BoundingBox = _graph.PumpTheBoxToTheGraphWithMargins(); }
public static GeometryGraph CreateFromFile(string fileName, out LayoutAlgorithmSettings settings) { #if DEBUG && TEST_MSAGL if (FirstCharacter(fileName) != '<') { settings = null; return null; } #endif using (Stream stream = File.OpenRead(fileName)) { var graphReader = new GeometryGraphReader(stream); GeometryGraph graph = graphReader.Read(); settings = graphReader.Settings; return graph; } }
/// <summary> /// Pack the given graph components to the specified aspect ratio /// </summary> /// <param name="components">set of graphs to pack</param> /// <param name="settings">settings for packing method and desired aspect ratio</param> /// <returns>Bounding box of the packed components</returns> public static Rectangle PackGraphs(IEnumerable<GeometryGraph> components, LayoutAlgorithmSettings settings) { List<RectangleToPack<GeometryGraph>> rectangles = (from c in components select new RectangleToPack<GeometryGraph>(c.BoundingBox, c)).ToList(); if (rectangles.Count > 1) { OptimalPacking<GeometryGraph> packing = settings.PackingMethod == PackingMethod.Compact ? new OptimalRectanglePacking<GeometryGraph>(rectangles, settings.PackingAspectRatio) : (OptimalPacking<GeometryGraph>) new OptimalColumnPacking<GeometryGraph>(rectangles, settings.PackingAspectRatio); packing.Run(); foreach (var r in rectangles) { GeometryGraph component = r.Data; var delta = r.Rectangle.LeftBottom - component.boundingBox.LeftBottom; component.Translate(delta); } return new Rectangle(0, 0, packing.PackedWidth, packing.PackedHeight); } if (rectangles.Count == 1) return rectangles[0].Rectangle; return Rectangle.CreateAnEmptyBox(); }