public void MinimumSizeIsRespected() { // Setup string filePath = Path.Combine(this.TestContext.TestDir, "Out\\Dots", "chat.dot"); GeometryGraph graph = this.LoadGraph(filePath); const double DesiredHeight = 100000; const double DesiredWidth = 100000; SugiyamaLayoutSettings settings = new SugiyamaLayoutSettings(); settings.MinimalHeight = DesiredHeight; settings.MinimalWidth = DesiredWidth; // Execute LayeredLayout layeredLayout = new LayeredLayout(graph, settings); layeredLayout.Run(); // Verify the graph is the correct size Assert.IsTrue(DesiredHeight < graph.Height, "Graph height should be the minimal height."); Assert.IsTrue(DesiredWidth < graph.Width, "Graph width should be the minimal width."); // Verify the nodes were spread apart to fill the space Rectangle nodeBounds = new Rectangle(graph.Nodes.Select(n => n.BoundingBox)); Assert.IsTrue(DesiredWidth < nodeBounds.Height, "The graph nodes weren't scaled vertically to fill the space."); Assert.IsTrue(DesiredWidth < nodeBounds.Width, "The graph nodes weren't scaled horizontally to fill the space."); }
internal ConstrainedOrdering( GeometryGraph geomGraph, BasicGraph<Node, IntEdge> basicIntGraph, int[] layering, Dictionary<Node, int> nodeIdToIndex, Database database, SugiyamaLayoutSettings settings) { this.settings = settings; horizontalConstraints = settings.HorizontalConstraints; horizontalConstraints.PrepareForOrdering(nodeIdToIndex, layering); geometryGraph = geomGraph; this.database = database; intGraph = basicIntGraph; initialLayering = layering; //this has to be changed only to insert layers that are needed if (NeedToInsertLayers(layering)) { for (int i = 0; i < layering.Length; i++) layering[i] *= 2; LayersAreDoubled = true; numberOfLayers = -1; } PrepareProperLayeredGraphAndFillLayerInfos(); adjSwapper = new AdjacentSwapsWithConstraints( LayerArrays, HasCrossWeights(), ProperLayeredGraph, layerInfos); }
internal PhyloTreeLayoutCalclulation(PhyloTree phyloTreeP, SugiyamaLayoutSettings settings, BasicGraph<Node, IntEdge> intGraphP, Database dataBase) { this.dataBase = dataBase; this.tree = phyloTreeP; this.LayoutSettings = settings; this.intGraph = intGraphP; originalNodeToGridLayerIndices = new int[intGraph.Nodes.Count]; }
public void NodeShapeChange() { // Setup string filePath = Path.Combine(this.TestContext.TestDir, "Out\\Dots", "chat.dot"); GeometryGraph graph = this.LoadGraph(filePath); var settings = new SugiyamaLayoutSettings(); // Initial layout LayeredLayout layeredLayout = new LayeredLayout(graph, settings); layeredLayout.Run(); SortedList<double, SortedList<double, Node>> originalLayers = SugiyamaValidation.GetLayers(graph, true); // Incremental layout List<Node> nodes = graph.Nodes.ToList(); for (int i = 0; i < nodes.Count; i++) { // Resize a node Node node = nodes[i]; node.BoundaryCurve = node.BoundaryCurve.ScaleFromOrigin(2.0, 2.0); // Run incremental layout LayeredLayout.IncrementalLayout(graph, node); // Verify - the layering and ordering of nodes should not have changed. SortedList<double, SortedList<double, Node>> newLayers = SugiyamaValidation.GetLayers(graph, true); VerifyLayersAreEqual(originalLayers, newLayers); } }
internal TwoLayerFlatEdgeRouter(SugiyamaLayoutSettings settings, Routing routing, int[] bottomLayer, int[] topLayer) { this.settings = settings; this.topLayer = topLayer; this.bottomLayer = bottomLayer; this.routing = routing; InitLabelsInfo(); }
/// <summary> /// Creates a smoothed polyline /// </summary> internal SmoothedPolylineCalculator(IntEdge edgePathPar, Anchor[] anchorsP, GeometryGraph origGraph, SugiyamaLayoutSettings settings, LayerArrays la, ProperLayeredGraph layerGraph, Database databaseP) { this.database = databaseP; edgePath = edgePathPar; anchors = anchorsP; this.layerArrays = la; this.originalGraph = origGraph; this.settings = settings; this.layeredGraph = layerGraph; rightHierarchy = BuildRightHierarchy(); leftHierarchy = BuildLeftHierarchy(); }
internal Routing(SugiyamaLayoutSettings settings, GeometryGraph originalGraph, Database dbP, LayerArrays yLayerArrays, ProperLayeredGraph properLayeredGraph, BasicGraph<Node, IntEdge> intGraph ) { this.settings = settings; OriginalGraph = originalGraph; Database = dbP; ProperLayeredGraph = properLayeredGraph; LayerArrays = yLayerArrays; IntGraph = intGraph; }
internal Routing(SugiyamaLayoutSettings settings, GeometryGraph originalGraph, Database dbP, LayerArrays yLayerArrays, ProperLayeredGraph properLayeredGraph, BasicGraph<Node, PolyIntEdge> intGraph ) { this.settings = settings; OriginalGraph = originalGraph; Database = dbP; ProperLayeredGraph = properLayeredGraph; LayerArrays = yLayerArrays; IntGraph = intGraph; }
internal static GeometryGraph CreateAndLayoutGraph() { PhyloTree phyloTree = new PhyloTree(); double width = 40; double height = 10; foreach (string id in "A B C D E F G".Split(' ')) DrawingUtilsForSamples.AddNode(id, phyloTree, width, height); PhyloEdge e; double age_of_BC = 2; double age_of_D = 3.5; double age_of_F = 1.5; double age_of_G = 3.5; double age_of_E = 2; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("A"), phyloTree.FindNodeByUserData("B"))); e.Length = age_of_BC; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("A"), phyloTree.FindNodeByUserData("C"))); e.Length = age_of_BC; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("A"), phyloTree.FindNodeByUserData("D"))); e.Length = age_of_D; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("C"), phyloTree.FindNodeByUserData("E"))); e.Length = age_of_E; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("C"), phyloTree.FindNodeByUserData("F"))); e.Length = age_of_F; phyloTree.Edges.Add(e = new PhyloEdge(phyloTree.FindNodeByUserData("C"), phyloTree.FindNodeByUserData("G"))); e.Length = age_of_G; var sugiyamaLayoutSettings = new SugiyamaLayoutSettings(); foreach (var edge in phyloTree.Edges) { edge.EdgeGeometry.TargetArrowhead = new Arrowhead(); } Microsoft.Msagl.Miscellaneous.LayoutHelpers.CalculateLayout(phyloTree, new SugiyamaLayoutSettings(), null); // add a couple of non-tree edges Edge e0 = new Edge(phyloTree.FindNodeByUserData("F"), phyloTree.FindNodeByUserData("D")) { EdgeGeometry = {SourceArrowhead = new Arrowhead()} }; phyloTree.Edges.Add(e0); Edge e1 = new Edge(phyloTree.FindNodeByUserData("G"), phyloTree.FindNodeByUserData("D")) { EdgeGeometry = {SourceArrowhead = new Arrowhead()} }; phyloTree.Edges.Add(e1); // route the non-tree edges, every other edge is routed already double loosePadding = sugiyamaLayoutSettings.NodeSeparation/10; double tightPadding = sugiyamaLayoutSettings.NodeSeparation/10; double coneAngle = Math.PI/6; var router = new SplineRouter(phyloTree, new[] {e0, e1}, tightPadding, loosePadding, coneAngle, null); router.Run(); return phyloTree; }
Ordering(ProperLayeredGraph graphPar, bool tryReverse, LayerArrays layerArraysParam, int startOfVirtualNodes, bool balanceVirtualAndOrigNodes, bool hasCrossWeights, SugiyamaLayoutSettings settings) { this.tryReverse = tryReverse; startOfVirtNodes = startOfVirtualNodes; layerArrays = layerArraysParam; layering = layerArraysParam.Y; nOfLayers = layerArraysParam.Layers.Length; layers = layerArraysParam.Layers; balanceVirtAndOrigNodes = balanceVirtualAndOrigNodes; properLayeredGraph = graphPar; this.hasCrossWeights = hasCrossWeights; this.settings = settings; random = new Random(SeedOfRandom); }
Ordering(ProperLayeredGraph graphPar, bool tryReverse, LayerArrays layerArraysParam, int startOfVirtualNodes, bool hasCrossWeights, SugiyamaLayoutSettings settings) { this.tryReverse = tryReverse; startOfVirtNodes = startOfVirtualNodes; layerArrays = layerArraysParam; layering = layerArraysParam.Y; nOfLayers = layerArraysParam.Layers.Length; layers = layerArraysParam.Layers; properLayeredGraph = graphPar; this.hasCrossWeights = hasCrossWeights; this.settings = settings; random = new Random(SeedOfRandom); }
/// <summary> /// it is a special recovery constructor to recreate the engine from the recovery engine /// </summary> internal LayeredLayoutEngine(LayerArrays engineLayerArrays, GeometryGraph originalGraph, ProperLayeredGraph properLayeredGraph, SugiyamaLayoutSettings sugiyamaSettings, Database database, BasicGraph<Node, IntEdge> intGraph, Dictionary<Node, int> nodeIdToIndex, BasicGraph<Node, IntEdge> gluedDagSkeletonForLayering, bool layersAreDoubled, ConstrainedOrdering constrainedOrdering, bool brandes, XLayoutGraph xLayoutGraph) { this.engineLayerArrays = engineLayerArrays; this.originalGraph = originalGraph; this.properLayeredGraph = properLayeredGraph; this.sugiyamaSettings = sugiyamaSettings; this.database = database; IntGraph = intGraph; this.nodeIdToIndex = nodeIdToIndex; GluedDagSkeletonForLayering = gluedDagSkeletonForLayering; LayersAreDoubled = layersAreDoubled; this.constrainedOrdering = constrainedOrdering; Brandes = brandes; anchors = database.anchors; this.xLayoutGraph = xLayoutGraph; }
public void TallRatioSimpleStretch() { // Setup string filePath = Path.Combine(this.TestContext.TestDir, "Out\\Dots", "chat.dot"); GeometryGraph graph = this.LoadGraph(filePath); SugiyamaLayoutSettings settings = new SugiyamaLayoutSettings(); settings.AspectRatio = 0.25; // Execute LayeredLayout layeredLayout = new LayeredLayout(graph, settings); layeredLayout.Run(); // Verify VerifyAspectRatio(graph, settings.AspectRatio); }
public void SerializeDeserialize() { // Create settings that are different from the defaults SugiyamaLayoutSettings oldSettings = new SugiyamaLayoutSettings(); oldSettings.AspectRatio++; oldSettings.LayerSeparation++; oldSettings.RepetitionCoefficientForOrdering++; oldSettings.RandomSeedForOrdering++; oldSettings.NoGainAdjacentSwapStepsBound++; oldSettings.MaxNumberOfPassesInOrdering++; oldSettings.GroupSplit++; oldSettings.LabelCornersPreserveCoefficient++; oldSettings.BrandesThreshold++; oldSettings.MinimalWidth++; oldSettings.MinimalHeight++; oldSettings.NodeSeparation++; oldSettings.MinNodeHeight++; oldSettings.MinNodeWidth++; oldSettings.LayeringOnly = !oldSettings.LayeringOnly; // Serialize GeometryGraph oldGraph = new GeometryGraph(); oldGraph.Nodes.Add(new Node()); GeometryGraphWriter.Write(oldGraph, oldSettings, "settings.msagl.geom"); // Deserialize LayoutAlgorithmSettings baseSettings; GeometryGraphReader.CreateFromFile("settings.msagl.geom", out baseSettings); SugiyamaLayoutSettings newSettings = (SugiyamaLayoutSettings)baseSettings; // Verify Assert.AreEqual(oldSettings.AspectRatio, newSettings.AspectRatio, "AspectRatio was not serialized correctly."); Assert.AreEqual(oldSettings.LayerSeparation, newSettings.LayerSeparation, "LayerSeparation was not serialized correctly."); Assert.AreEqual(oldSettings.LayeringOnly, newSettings.LayeringOnly, "LayeringOnly was not serialized correctly."); Assert.AreEqual(oldSettings.RepetitionCoefficientForOrdering, newSettings.RepetitionCoefficientForOrdering, "RepetitionCoefficientForOrdering was not serialized correctly."); Assert.AreEqual(oldSettings.RandomSeedForOrdering, newSettings.RandomSeedForOrdering, "RandomSeedForOrdering was not serialized correctly."); Assert.AreEqual(oldSettings.NoGainAdjacentSwapStepsBound, newSettings.NoGainAdjacentSwapStepsBound, "NoGainAdjacentSwapStepsBound was not serialized correctly."); Assert.AreEqual(oldSettings.MaxNumberOfPassesInOrdering, newSettings.MaxNumberOfPassesInOrdering, "MaxNumberOfPassesInOrdering was not serialized correctly."); Assert.AreEqual(oldSettings.GroupSplit, newSettings.GroupSplit, "GroupSplit was not serialized correctly."); Assert.AreEqual(oldSettings.LabelCornersPreserveCoefficient, newSettings.LabelCornersPreserveCoefficient, "LabelCornersPreserveCoefficient was not serialized correctly."); Assert.AreEqual(oldSettings.BrandesThreshold, newSettings.BrandesThreshold, "BrandesThreshold was not serialized correctly."); Assert.AreEqual(oldSettings.MinimalWidth, newSettings.MinimalWidth, "MinimalWidth was not serialized correctly."); Assert.AreEqual(oldSettings.MinimalHeight, newSettings.MinimalHeight, "MinimalHeight was not serialized correctly."); Assert.AreEqual(oldSettings.NodeSeparation, newSettings.NodeSeparation, "NodeSeparation was not serialized correctly."); Assert.AreEqual(oldSettings.MinNodeHeight, newSettings.MinNodeHeight, "MinNodeHeight was not serialized correctly."); Assert.AreEqual(oldSettings.MinNodeWidth, newSettings.MinNodeWidth, "MinNodeWidth was not serialized correctly."); }
/// <summary> /// Validate all layers have valid separation /// </summary> internal static void ValidateLayerSeparation(GeometryGraph graph, SugiyamaLayoutSettings settings) { bool isVertical = IsVerticallyLayered(settings.Transformation); SortedList<double, SortedList<double, Node>> layers = GetLayers(graph, isVertical); #if TEST_MSAGL Console.WriteLine("Setting for layer separation: " + settings.LayerSeparation); for (int j = 0; j < layers.Count; j++) { SortedList<double, Node> layer = layers.Values[j]; Console.WriteLine("Layer No. " + (j + 1)); for (int k = 0; k < layer.Count; k++) { Node node = layer.Values[k]; Console.WriteLine("\t On this layer, No. " + (k + 1) + " Node's boundary is " + node.BoundingBox); } } #endif //Make sure no src/target of one edge stay in same layer foreach (IList<Node> layer in layers.Values.Select(l => l.Values)) { ValidateSourceTargetNotOnSameLayer(layer); } Tuple<Node, Node>[] layerNodes = new Tuple<Node, Node>[layers.Keys.Count]; for (int i = 0; i < layers.Keys.Count; i++) { layerNodes[i] = GetBiggestNodesInLayer(layers.Values[i].Values, isVertical); } for (int i = 1; i < layers.Keys.Count - 1; i++) { if (isVertical) { Assert.IsTrue(Math.Abs(layerNodes[i - 1].Item2.BoundingBox.Bottom - layerNodes[i].Item1.BoundingBox.Top) + Tolerance >= settings.LayerSeparation, string.Format("layer {0} not having right separation with layer {1}", i - 1, i)); Assert.IsTrue(Math.Abs(layerNodes[i + 1].Item2.BoundingBox.Top - layerNodes[i].Item1.BoundingBox.Bottom) + Tolerance >= settings.LayerSeparation, string.Format("layer {0} not having right separation with layer {1}", i + 1, i)); } else { Assert.IsTrue(Math.Abs(layerNodes[i - 1].Item2.BoundingBox.Right - layerNodes[i].Item1.BoundingBox.Left) + Tolerance >= settings.LayerSeparation, string.Format("layer {0} not having right separation with layer {1}", i - 1, i)); Assert.IsTrue(Math.Abs(layerNodes[i + 1].Item2.BoundingBox.Left - layerNodes[i].Item1.BoundingBox.Right) + Tolerance >= settings.LayerSeparation, string.Format("layer {0} not having right separation with layer {1}", i + 1, i)); } } }
internal static GeometryGraph CreateAndLayoutGraph() { GeometryGraph graph = new GeometryGraph(); double width = 40; double height = 10; foreach (string id in "0 1 2 3 4 5 6 A B C D E F G a b c d e".Split(' ')) { DrawingUtilsForSamples.AddNode(id, graph, width, height); } graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("B"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("C"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("D"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("E"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("E"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("0"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("1"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("2"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("3"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("4"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("5"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("6"), graph.FindNodeByUserData("F"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("a"), graph.FindNodeByUserData("b"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("b"), graph.FindNodeByUserData("c"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("c"), graph.FindNodeByUserData("d"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("d"), graph.FindNodeByUserData("e"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("C"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("E"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("F"), graph.FindNodeByUserData("a"))); graph.Edges.Add(new Edge(graph.FindNodeByUserData("G"), graph.FindNodeByUserData("a"))); var settings = new SugiyamaLayoutSettings { Transformation = PlaneTransformation.Rotation(Math.PI/2), EdgeRoutingSettings = {EdgeRoutingMode = EdgeRoutingMode.Spline} }; var layout = new LayeredLayout(graph, settings); layout.Run(); return graph; }
/// <summary> /// Default constructor /// </summary> public GViewer(){ mdsLayoutSettings = new MdsLayoutSettings(); sugiyamaSettings = new SugiyamaLayoutSettings(); // This call is required by the Windows.Forms Form Designer. InitializeComponent(); BackwardEnabled = false; ForwardEnabled = false; toolbar.MouseMove += ToolBarMouseMoved; Assembly a = Assembly.GetExecutingAssembly(); foreach (string r in a.GetManifestResourceNames()){ if (r.Contains("hmove.cur")) panGrabCursor = new Cursor(a.GetManifestResourceStream(r)); else if (r.Contains("oph.cur")) panOpenCursor = new Cursor(a.GetManifestResourceStream(r)); } originalCursor = Cursor; panButton.Pushed = false; windowZoomButton.Pushed = false; layoutSettingsButton.ToolTipText = "Configures the layout algorithm settings"; undoButton.ToolTipText = "Undo layout editing"; redoButton.ToolTipText = "Redo layout editing"; forwardButton.ToolTipText = "Forward"; panButton.ToolTipText = panButton.Pushed ? panButtonToolTipText : PanButtonDisabledToolTipText; windowZoomButton.ToolTipText = windowZoomButton.Pushed ? WindowZoomButtonToolTipText : windowZoomButtonDisabledToolTipText; InitDrawingLayoutEditor(); toolbar.Invalidate(); SuspendLayout(); InitPanel(); Controls.Add(toolbar); ResumeLayout(); }
internal static void OrderLayers(ProperLayeredGraph graph, LayerArrays layerArrays, int startOfVirtualNodes, SugiyamaLayoutSettings settings, CancelToken cancelToken) { bool hasCrossWeight = false; foreach (LayerEdge le in graph.Edges) { if (le.CrossingWeight != 1) { hasCrossWeight = true; break; } } var o = new Ordering(graph, true, layerArrays, startOfVirtualNodes, hasCrossWeight, settings); o.Run(cancelToken); }
internal static GeometryGraph CreateAndLayoutGraph() { double w = 40; double h = 10; GeometryGraph graph = new GeometryGraph(); // columns var col0 = new[] { "a", "b", "c" }; var col1 = new[] { "d", "e", "f", "g" }; var col2 = new[] { "k", "l", "m", "n" }; var col3 = new[] { "w", "y", "z" }; var settings = new SugiyamaLayoutSettings(); foreach (var id in col0) DrawingUtilsForSamples.AddNode(id, graph, w, h); foreach (var id in col1) DrawingUtilsForSamples.AddNode(id, graph, w, h); foreach (var id in col2) DrawingUtilsForSamples.AddNode(id, graph, w, h); foreach (var id in col3) DrawingUtilsForSamples.AddNode(id, graph, w, h); //pinning columns settings.PinNodesToSameLayer(col0.Select(s => graph.FindNodeByUserData(s)).ToArray()); settings.PinNodesToSameLayer(col1.Select(s => graph.FindNodeByUserData(s)).ToArray()); settings.PinNodesToSameLayer(col2.Select(s => graph.FindNodeByUserData(s)).ToArray()); settings.PinNodesToSameLayer(col3.Select(s => graph.FindNodeByUserData(s)).ToArray()); AddEdgesBetweenColumns(col0, col1, graph); AddEdgesBetweenColumns(col1, col2, graph); AddEdgesBetweenColumns(col2, col3, graph); // rotate layer to columns settings.Transformation = PlaneTransformation.Rotation(Math.PI/2); settings.NodeSeparation = 5; settings.LayerSeparation = 100; var ll = new LayeredLayout(graph, settings); ll.Run(); return graph; }
/// <summary> /// Validate two nodes on same layer have valid separation between them /// </summary> internal static void ValidateIntraLayerNodeSeparation(Node node, Node node2, SugiyamaLayoutSettings settings) { if (node == node2 || node is Cluster || node2 is Cluster) { return; } if (OnSameLayer(node, node2, settings)) { if (Math.Abs(node.Center.Y - node2.Center.Y) <= Tolerance) { Rectangle left = node.BoundingBox; Rectangle right = node2.BoundingBox; Rectangle temp; if (left.Left > right.Left) { temp = left; left = right; right = temp; } Assert.IsTrue(Math.Abs(right.Left - left.Right) >= settings.NodeSeparation, string.Format("Node (ID: {0}, BoundingBox: {1}) has less separation from Node (ID: {2}, BoundingBox: {3})", node.UserData, node.BoundingBox.ToString(), node2.UserData, node2.BoundingBox.ToString())); } else { Rectangle top = node.BoundingBox; Rectangle bottom = node2.BoundingBox; Rectangle temp; if (top.Top < bottom.Top) { temp = top; top = bottom; bottom = temp; } Assert.IsTrue(Math.Abs(top.Bottom - bottom.Top) >= settings.NodeSeparation, string.Format("Node (ID: {0}, BoundingBox: {1}) has less separation from Node (ID: {2}, BoundingBox: {3})", node.UserData, node.BoundingBox.ToString(), node2.UserData, node2.BoundingBox.ToString())); } } }
internal ConstrainedOrdering( GeometryGraph geomGraph, BasicGraph <Node, IntEdge> basicIntGraph, int[] layering, Dictionary <Node, int> nodeIdToIndex, Database database, SugiyamaLayoutSettings settings) { this.settings = settings; horizontalConstraints = settings.HorizontalConstraints; horizontalConstraints.PrepareForOrdering(nodeIdToIndex, layering); geometryGraph = geomGraph; this.database = database; intGraph = basicIntGraph; initialLayering = layering; //this has to be changed only to insert layers that are needed if (NeedToInsertLayers(layering)) { for (int i = 0; i < layering.Length; i++) { layering[i] *= 2; } LayersAreDoubled = true; numberOfLayers = -1; } PrepareProperLayeredGraphAndFillLayerInfos(); adjSwapper = new AdjacentSwapsWithConstraints( LayerArrays, HasCrossWeights(), ProperLayeredGraph, layerInfos); }
static void RightAnchorMultiSelfEdges(int i, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor, Database database, SugiyamaLayoutSettings settings) { double delta = WidthOfSelfEdge(database, i, ref rightAnchor, ref topAnchor, ref bottomAnchor, settings); rightAnchor += delta; }
static void CalcAnchorsForOriginalNode(int i, BasicGraph<Node, IntEdge> intGraph, Anchor[] anchors, Database database, SugiyamaLayoutSettings settings) { double leftAnchor = 0; double rightAnchor = leftAnchor; double topAnchor = 0; double bottomAnchor = topAnchor; //that's what we would have without the label and multiedges if (intGraph.Nodes != null) { Node node = intGraph.Nodes[i]; ExtendStandardAnchors(ref leftAnchor, ref rightAnchor, ref topAnchor, ref bottomAnchor, node, settings); } RightAnchorMultiSelfEdges(i, ref rightAnchor, ref topAnchor, ref bottomAnchor, database, settings); double hw = settings.MinNodeWidth/2; if (leftAnchor < hw) leftAnchor = hw; if (rightAnchor < hw) rightAnchor = hw; double hh = settings.MinNodeHeight/2; if (topAnchor < hh) topAnchor = hh; if (bottomAnchor < hh) bottomAnchor = hh; anchors[i] = new Anchor(leftAnchor, rightAnchor, topAnchor, bottomAnchor, intGraph.Nodes[i], settings.LabelCornersPreserveCoefficient) {Padding = intGraph.Nodes[i].Padding}; #if TEST_MSAGL anchors[i].UserData = intGraph.Nodes[i].UserData; #endif }
internal static Directions GetLayoutDirection(SugiyamaLayoutSettings settings) { Point dir = settings.Transformation*new Point(0, 1); return dir.CompassDirection; }
static double SetFlatEdgesForLayer(Database database, LayerArrays layerArrays, int i, BasicGraph<IntEdge> intGraph, SugiyamaLayoutSettings settings, double ymax) { double flatEdgesHeight = 0; if (i > 0) { //looking for flat edges on the previous level //we stack labels of multiple flat edges on top of each other IEnumerable<IntPair> flatPairs = GetFlatPairs(layerArrays.Layers[i - 1], layerArrays.Y, intGraph); if (flatPairs.Any()) { double dyOfFlatEdge = settings.LayerSeparation/3; double ym = ymax; flatEdgesHeight = (from pair in flatPairs select SetFlatEdgesLabelsHeightAndPositionts(pair, ym, dyOfFlatEdge, database)). Max(); } } return flatEdgesHeight; }
private static bool NeedToSnapBottomsToGrid(SugiyamaLayoutSettings settings) { return settings.SnapToGridByY == SnapToGridByY.Bottom; }
/// <summary> /// /// </summary> /// <returns>the height of the graph+spaceBeforeMargins</returns> internal static void CalcInitialYAnchorLocations(LayerArrays layerArrays, double spaceBeforeMargins, GeometryGraph originalGraph, Database database, BasicGraph<IntEdge> intGraph, SugiyamaLayoutSettings settings, bool layersAreDoubled) { Anchor[] anchors = database.Anchors; double ymax = originalGraph.Margins + spaceBeforeMargins; //setting up y coord - going up by y-layers int i = 0; foreach (var yLayer in layerArrays.Layers) { double bottomAnchorMax = 0; double topAnchorMax = 0; foreach (int j in yLayer) { Anchor p = anchors[j]; if (p.BottomAnchor > bottomAnchorMax) bottomAnchorMax = p.BottomAnchor; if (p.TopAnchor > topAnchorMax) topAnchorMax = p.TopAnchor; } MakeVirtualNodesTall(yLayer, bottomAnchorMax, topAnchorMax, originalGraph.Nodes.Count, database.Anchors); double flatEdgesHeight = SetFlatEdgesForLayer(database, layerArrays, i, intGraph, settings, ymax); double layerCenter = ymax + bottomAnchorMax + flatEdgesHeight; double layerTop = layerCenter + topAnchorMax; if (NeedToSnapTopsToGrid(settings)) { layerTop += SnapDeltaUp(layerTop, settings.GridSizeByY); foreach (int j in yLayer) anchors[j].Top = layerTop; } else if (NeedToSnapBottomsToGrid(settings)) { double layerBottom = layerCenter - bottomAnchorMax; layerBottom += SnapDeltaUp(layerBottom, layerBottom); foreach (int j in yLayer) { anchors[j].Bottom = layerBottom; layerTop = Math.Max(anchors[j].Top, layerTop); } } else foreach (int j in yLayer) anchors[j].Y = layerCenter; double layerSep = settings.ActualLayerSeparation(layersAreDoubled); ymax = layerTop + layerSep; i++; } // for the last layer SetFlatEdgesForLayer(database, layerArrays, i, intGraph, settings, ymax); }
/// <summary> /// Simpler constructor which initializes the internal graph representation automatically /// </summary> /// <param name="originalGraph"></param> /// <param name="settings"></param> internal LayeredLayoutEngine(GeometryGraph originalGraph, SugiyamaLayoutSettings settings) { if (originalGraph != null) { //enumerate the nodes - maps node indices to strings nodeIdToIndex = new Dictionary<Node, int>(); IList<Node> nodes = originalGraph.Nodes; int index = 0; foreach (Node n in nodes) { nodeIdToIndex[n] = index; index++; } var edges = originalGraph.Edges; var intEdges = new IntEdge[edges.Count]; int i = 0; foreach(var edge in edges){ if (edge.Source == null || edge.Target == null) throw new InvalidOperationException(); //"creating an edge with null source or target"); var intEdge = new IntEdge(nodeIdToIndex[edge.Source], nodeIdToIndex[edge.Target], edge); intEdges[i] = intEdge; i++; } IntGraph = new BasicGraph<Node, IntEdge>(intEdges, originalGraph.Nodes.Count) {Nodes = nodes}; this.originalGraph = originalGraph; sugiyamaSettings = settings; Database = new Database(); foreach (IntEdge e in IntGraph.Edges) database.RegisterOriginalEdgeInMultiedges(e); CycleRemoval(); } }
internal static void CalculateAnchorSizes(Database database, out Anchor[] anchors, ProperLayeredGraph properLayeredGraph, GeometryGraph originalGraph, BasicGraph<Node, IntEdge> intGraph, SugiyamaLayoutSettings settings) { database.Anchors = anchors = new Anchor[properLayeredGraph.NodeCount]; for (int i = 0; i < anchors.Length; i++) anchors[i] = new Anchor(settings.LabelCornersPreserveCoefficient); //go over the old vertices for (int i = 0; i < originalGraph.Nodes.Count; i++) CalcAnchorsForOriginalNode(i, intGraph, anchors, database, settings); //go over virtual vertices foreach (IntEdge intEdge in database.AllIntEdges) if (intEdge.LayerEdges != null) { foreach (LayerEdge layerEdge in intEdge.LayerEdges) { int v = layerEdge.Target; if (v != intEdge.Target) { Anchor anchor = anchors[v]; if (!database.MultipleMiddles.Contains(v)) { anchor.LeftAnchor = anchor.RightAnchor = VirtualNodeWidth/2.0f; anchor.TopAnchor = anchor.BottomAnchor = VirtualNodeHeight(settings)/2.0f; } else { anchor.LeftAnchor = anchor.RightAnchor = VirtualNodeWidth*4; anchor.TopAnchor = anchor.BottomAnchor = VirtualNodeHeight(settings)/2.0f; } } } //fix label vertices if (intEdge.HasLabel) { int lj = intEdge.LayerEdges[intEdge.LayerEdges.Count/2].Source; Anchor a = anchors[lj]; double w = intEdge.LabelWidth, h = intEdge.LabelHeight; a.RightAnchor = w; a.LeftAnchor = VirtualNodeWidth*8; if (a.TopAnchor < h/2.0) a.TopAnchor = a.BottomAnchor = h/2.0; a.LabelToTheRightOfAnchorCenter = true; } } }
/// <summary> /// constructor /// </summary> internal LayeredLayoutEngine(GeometryGraph originalGraph, BasicGraph<Node, IntEdge> graph, Dictionary<Node, int> nodeIdToIndex, SugiyamaLayoutSettings settings) { if (originalGraph != null) { this.originalGraph = originalGraph; sugiyamaSettings = settings; IntGraph = graph; Database = new Database(); this.nodeIdToIndex = nodeIdToIndex; foreach (IntEdge e in graph.Edges) database.RegisterOriginalEdgeInMultiedges(e); #if REPORTING if (sugiyamaSettings.Reporting && SugiyamaLayoutLogger == null) SugiyamaLayoutLogger = new SugiyamaLayoutLogger(); #endif CycleRemoval(); } }
static double WidthOfSelfEdge(Database database, int i, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor, SugiyamaLayoutSettings settings) { double delta = 0; List<IntEdge> multiedges = database.GetMultiedge(i, i); //it could be a multiple self edge if (multiedges.Count > 0) { foreach (IntEdge e in multiedges) if (e.Edge.Label != null) { rightAnchor += e.Edge.Label.Width; if (topAnchor < e.Edge.Label.Height/2.0) topAnchor = bottomAnchor = e.Edge.Label.Height/2.0f; } delta += (settings.NodeSeparation + settings.MinNodeWidth)*multiedges.Count; } return delta; }
//takes a target Tree object and builds actual game nodes for it - using MSAGL as an intermediate representation for layout public void BuildTree(Tree target) { if (target == null) { return; //do not attempt to build a null tree - this means that a syntax error happened in a newick file } GameManager.DebugLog("Building tree with layout: " + eventManager.Current_Tree_State); ResetTreeContainer(); GeometryGraph asMSAGL = ToMSALGraph(target); List <GameObject> generatedNodes = new List <GameObject>(); //define how we want the tree to be layed out LayoutAlgorithmSettings settings; switch (eventManager.Current_Tree_State) { default: case InputEventManager.TreeState.BasicTree: settings = new Microsoft.Msagl.Layout.Layered.SugiyamaLayoutSettings(); break; case InputEventManager.TreeState.TerrainTree: case InputEventManager.TreeState.CircularTree: settings = new FastIncrementalLayoutSettings(); break; } //apply some extra settings and layout the graph according to all settings settings.EdgeRoutingSettings.EdgeRoutingMode = Microsoft.Msagl.Core.Routing.EdgeRoutingMode.StraightLine; settings.PackingMethod = PackingMethod.Compact; LayoutHelpers.CalculateLayout(asMSAGL, settings, null); //we want world space 0, 0 to be equivalent to graph space 0, 0 float offSetX = (float)asMSAGL.Nodes[0].Center.X; float offSetY = (float)asMSAGL.Nodes[0].Center.Y; //msal graph edge weights are enormous, like hundreds of meters of in world space - scale it down float scaleFactor = eventManager.Current_Tree_State == InputEventManager.TreeState.TerrainTree ? 1f / 30f : 1f / 250f; //build game objects using msagl graph as spatial layout foreach (Microsoft.Msagl.Core.Layout.Node node in asMSAGL.Nodes) { float x = ((float)node.Center.X - offSetX) * scaleFactor; float y = ((float)node.Center.Y - offSetY) * scaleFactor; GameObject newNode = Instantiate(nodePrefab, new Vector3( treeContainer.transform.position.x + x, treeContainer.transform.position.y + y, treeContainer.transform.position.z ), Quaternion.identity); newNode.transform.SetParent(treeContainer.transform); newNode.GetComponent <BasicNodeBehaviour>().attachNodeInfo((Node)node.UserData); newNode.GetComponentInChildren <InfoOnHover>()._Init(); node.UserData = newNode; generatedNodes.Add(newNode); foreach (Edge edge in node.Edges) { if (edge.Target == node) { Line l = newNode.transform.Find("Renderer").gameObject.AddComponent <Line>(); l.gameObject1 = newNode; l.gameObject2 = (GameObject)edge.Source.UserData; newNode.transform.SetParent(((GameObject)edge.Source.UserData).transform); if (newNode.GetComponent <BasicNodeBehaviour>().getAttachedNodeInfo().weightToParentSet()) { l.setLabel("" + newNode.GetComponent <BasicNodeBehaviour>().getAttachedNodeInfo().getWeightToParent()); } else { l.setLabel(""); } } } } if (eventManager.Current_Tree_State == InputEventManager.TreeState.TerrainTree) { Vector3 translation = new Vector3(0, -4, 0); treeContainer.transform.Translate(translation); treeContainer.transform.Rotate(90, 0, 0); List <GameObject> leafNodesList = new List <GameObject>(); foreach (GameObject node in generatedNodes) { //scale up the nodes a bit to make them clearer within the terrain float scale = node.GetComponentInChildren <MaintainNodeScale>().targetScale; node.GetComponentInChildren <MaintainNodeScale>().targetScale = 2 * scale; node.transform.hasChanged = true; if (node.GetComponent <BasicNodeBehaviour>().getAttachedNodeInfo().getNumberOfChildren() == 0) { leafNodesList.Add(node); } } generatedNodes[0].GetComponentInChildren <MaintainNodeScale>().targetScale = 4f; GameManager.DebugLog("Found " + leafNodesList.Count + " leaf nodes"); TreeTerrainBehaviour.instance.Activate(); TreeTerrainBehaviour.instance.ResetHeights(); leafNodes = leafNodesList.ToArray(); EnableOnFrameLoading(); } else { DisableOnFrameLoading(); //the player won't be moving while we aren't looking at terrain so we'll reset their position } if (eventManager.Draw_Tree_In_3D) { TreeConverter3D.Convert(treeContainer.transform.GetChild(0).gameObject); } //as a last step, reset the position of the player so they are close to the root of whatever tree they built player.GetComponent <PositionResetter>().ResetPosition(); }
void WriteSugiyamaSettings(SugiyamaLayoutSettings sugiyama) { WriteAttribute(GeometryToken.LayoutAlgorithmType, GeometryToken.SugiyamaLayoutSettings); WriteAttribute(GeometryToken.MinNodeWidth, sugiyama.MinNodeWidth); WriteAttribute(GeometryToken.MinNodeHeight, sugiyama.MinNodeHeight); WriteAttribute(GeometryToken.AspectRatio, sugiyama.AspectRatio); WriteAttribute(GeometryToken.NodeSeparation, sugiyama.NodeSeparation); #if REPORTING WriteAttribute(GeometryToken.Reporting, sugiyama.Reporting); #endif WriteAttribute(GeometryToken.RandomSeedForOrdering, sugiyama.RandomSeedForOrdering); WriteAttribute(GeometryToken.NoGainStepsBound, sugiyama.NoGainAdjacentSwapStepsBound); WriteAttribute(GeometryToken.MaxNumberOfPassesInOrdering, sugiyama.MaxNumberOfPassesInOrdering); WriteAttribute(GeometryToken.RepetitionCoefficientForOrdering, sugiyama.RepetitionCoefficientForOrdering); WriteAttribute(GeometryToken.GroupSplit, sugiyama.GroupSplit); WriteAttribute(GeometryToken.LabelCornersPreserveCoefficient, sugiyama.LabelCornersPreserveCoefficient); WriteAttribute(GeometryToken.BrandesThreshold, sugiyama.BrandesThreshold); WriteAttribute(GeometryToken.LayerSeparation, sugiyama.LayerSeparation); WriteTransformation(sugiyama.Transformation); }
static void ExtendStandardAnchors(ref double leftAnchor, ref double rightAnchor, ref double topAnchor, ref double bottomAnchor, Node node, SugiyamaLayoutSettings settings) { double w = node.Width; double h = node.Height; w /= 2.0; h /= 2.0; rightAnchor = leftAnchor = w; topAnchor = bottomAnchor = h; if (settings.GridSizeByX > 0) { rightAnchor += settings.GridSizeByX / 2; leftAnchor += settings.GridSizeByX / 2; } }
void Show() { SugiyamaLayoutSettings.ShowDatabase(database); }
/// <summary> /// the height of dummy nodes /// </summary> static double VirtualNodeHeight(SugiyamaLayoutSettings settings) { return settings.MinNodeHeight*1.5f/8; }
internal FlatEdgeRouter(SugiyamaLayoutSettings settings, Routing routing) { this.settings = settings; this.routing = routing; }
/// <summary> /// Layered layout arranged the given graph on layers inferred from the directed edge structure /// </summary> /// <param name="geometryGraph">graph to be laid out</param> /// <param name="settings">The settings for the algorithm.</param> public LayeredLayout(GeometryGraph geometryGraph, SugiyamaLayoutSettings settings) { this.geometryGraph = geometryGraph; this.settings = settings; this.engine = new LayeredLayoutEngine(geometryGraph, settings); }
internal static LayeredLayoutEngine CalculateLayout(GeometryGraph msaglGraph, SugiyamaLayoutSettings settings, CancelToken cancelToken) { LayeredLayoutEngine engine = new LayeredLayoutEngine(msaglGraph, settings); #if USE_PHYLOTREE PhyloTree phyloTree = msaglGraph as PhyloTree; if (phyloTree != null) { PhyloTreeLayoutCalclulation pc = new PhyloTreeLayoutCalclulation(phyloTree, settings, engine.IntGraph, engine.Database); pc.Run(); } else #endif engine.Run(cancelToken); return(engine); }