public void StringEscaping() { RootGraph root = RootGraph.CreateNew("Graph with escaped strings", GraphType.Directed); Node.IntroduceAttribute(root, "label", "\\N"); Node nodeA = root.GetOrAddNode("A"); // Several characters and character sequences can have special meanings in labels, like \N. // When you want to have a literal string in a label, we provide a convenience function for you to do just that. nodeA.SetAttribute("label", CGraphThing.EscapeLabel("Some string literal \\N \\n |}>")); root.ComputeLayout(); // When defining portnames, some characters, like ':' and '|', are not allowed and they can't be escaped either. // This can be troubling if you have an externally defined ID for such a port. // We provide a function that maps strings to valid portnames. var somePortId = "port id with :| special characters"; var validPortName = Edge.ConvertUidToPortName(somePortId); Node nodeB = root.GetOrAddNode("B"); nodeB.SafeSetAttribute("shape", "record", ""); nodeB.SafeSetAttribute("label", $"<{validPortName}>1|2", "\\N"); // The function makes sure different strings don't accidentally map onto the same portname Assert.That(Edge.ConvertUidToPortName(":"), Is.Not.EqualTo(Edge.ConvertUidToPortName("|"))); }
public void GraphConstruction() { // You can programmatically construct graphs as follows RootGraph root = RootGraph.CreateNew("Some Unique Identifier", GraphType.Directed); // The node names are unique identifiers within a graph in Graphviz Node nodeA = root.GetOrAddNode("A"); Node nodeB = root.GetOrAddNode("B"); Node nodeC = root.GetOrAddNode("C"); Node nodeD = root.GetOrAddNode("D"); // The edge name is only unique between two nodes Edge edgeAB = root.GetOrAddEdge(nodeA, nodeB, "Some edge name"); Edge edgeBC = root.GetOrAddEdge(nodeB, nodeC, "Some edge name"); Edge anotherEdgeBC = root.GetOrAddEdge(nodeB, nodeC, "Another edge name"); // We can attach attributes to nodes, edges and graphs to store information and instruct // graphviz by specifying layout parameters. At the moment we only support string // attributes. Cgraph assumes that all objects of a given kind (graphs/subgraphs, nodes, // or edges) have the same attributes. The attributes first have to be introduced for a // certain kind, before we can use it. Node.IntroduceAttribute(root, "my attribute", "defaultvalue"); nodeA.SetAttribute("my attribute", "othervalue"); // To introduce and set an attribute at the same time, there are convenience wrappers edgeAB.SafeSetAttribute("color", "red", "black"); edgeBC.SafeSetAttribute("arrowsize", "2.0", "1.0"); // We can simply export this graph to a text file in dot format root.ToDotFile(TestContext.CurrentContext.TestDirectory + "/out.dot"); }
public void Records() { RootGraph root = RootGraph.CreateNew("Graph with records", GraphType.Directed); Node nodeA = root.GetOrAddNode("A"); nodeA.SafeSetAttribute("shape", "record", ""); nodeA.SafeSetAttribute("label", "1|2|3|{4|5}|6|{7|8|9}", "\\N"); root.ComputeLayout(); // The order of the list matches the order in which the labels occur in the label string above. var rects = nodeA.GetRecordRectangles().ToList(); Assert.That(rects.Count, Is.EqualTo(9)); }
public void Clusters() { RootGraph root = RootGraph.CreateNew("Graph with clusters", GraphType.Directed); Node nodeA = root.GetOrAddNode("A"); Node nodeB = root.GetOrAddNode("B"); Node nodeC = root.GetOrAddNode("C"); Node nodeD = root.GetOrAddNode("D"); // When a subgraph name is prefixed with cluster, // the dot layout engine will render it as a box around the containing nodes. SubGraph cluster1 = root.GetOrAddSubgraph("cluster_1"); cluster1.AddExisting(nodeB); cluster1.AddExisting(nodeC); SubGraph cluster2 = root.GetOrAddSubgraph("cluster_2"); cluster2.AddExisting(nodeD); // COMPOUND EDGES // Graphviz does not really support edges from and to clusters. However, by adding an // invisible dummynode and setting the ltail or lhead attributes of an edge this // behavior can be faked. Graphviz will then draw an edge to the dummy node but clip it // at the border of the cluster. We provide convenience methods for this. // To enable this feature, Graphviz requires us to set the "compound" attribute to "true". Graph.IntroduceAttribute(root, "compound", "true"); // Allow lhead/ltail // The boolean indicates whether the dummy node should take up any space. When you pass // false and you have a lot of edges, the edges may start to overlap a lot. _ = root.GetOrAddEdge(nodeA, cluster1, false, "edge to a cluster"); _ = root.GetOrAddEdge(cluster1, nodeD, false, "edge from a cluster"); _ = root.GetOrAddEdge(cluster1, cluster1, false, "edge between clusters"); root.ComputeLayout(); SubGraph cluster = root.GetSubgraph("cluster_1"); RectangleF clusterbox = cluster.BoundingBox(); RectangleF rootgraphbox = root.BoundingBox(); Utils.AssertPattern(@"{X=[\d.]+,Y=[\d.]+,Width=[\d.]+,Height=[\d.]+}", clusterbox.ToString()); Utils.AssertPattern(@"{X=[\d.]+,Y=[\d.]+,Width=[\d.]+,Height=[\d.]+}", rootgraphbox.ToString()); }
public static RootGraph CreateUniqueTestGraph() { RootGraphCounter += 1; return(RootGraph.CreateNew("test graph " + RootGraphCounter.ToString(), GraphType.Directed)); }
protected override PlacementInfo PositionComponents(Dictionary <FIRRTLNode, Point> nodeSizes, Module mod) { PlacementInfo placments = new PlacementInfo(); const int dpi = 96; const int ppi = 72; RootGraph graph = RootGraph.CreateNew("some name??", GraphType.Directed); graph.SafeSetAttribute("rankdir", "LR", "TB"); graph.SafeSetAttribute("ranksep", "7", "0.5"); graph.SafeSetAttribute("nodesep", "1.0", "0.25"); //Add nodes to graph Dictionary <FIRRTLNode, Node> firNodeToNode = new Dictionary <FIRRTLNode, Node>(); Dictionary <Input, string> inputToPort = new Dictionary <Input, string>(); Dictionary <Output, string> outputToPort = new Dictionary <Output, string>(); Dictionary <Input, Node> inputToNode = new Dictionary <Input, Node>(); Dictionary <Output, Node> outputToNode = new Dictionary <Output, Node>(); int nodeCounter = 0; int portName = 0; foreach (var firNode in nodeSizes) { if (firNode.Key == mod) { continue; } string nodeName = $"n{nodeCounter++}"; Node node = graph.GetOrAddNode(nodeName); node.SafeSetAttribute("shape", "record", "ellipse"); node.SafeSetAttribute("width", ((double)firNode.Value.X / dpi).ToString(), "0.75"); node.SafeSetAttribute("height", ((double)firNode.Value.Y / dpi).ToString(), "0.5"); Input[] nodeInputs = firNode.Key.GetInputs(); Output[] nodeOutputs = firNode.Key.GetOutputs(); MakeIntoRecord(node, nodeInputs, nodeOutputs, inputToPort, outputToPort, ref portName); firNodeToNode.Add(firNode.Key, node); foreach (var input in nodeInputs) { inputToNode.Add(input, node); } foreach (var output in nodeOutputs) { outputToNode.Add(output, node); } } Node modInputNode = graph.GetOrAddNode("modInput"); MakeIntoRecord(modInputNode, mod.GetInputs(), Array.Empty <Output>(), inputToPort, outputToPort, ref portName); modInputNode.SafeSetAttribute("rank", "sink", "sink"); Node modOutputNode = graph.GetOrAddNode("modOutput"); MakeIntoRecord(modInputNode, Array.Empty <Input>(), mod.GetOutputs(), inputToPort, outputToPort, ref portName); modOutputNode.SafeSetAttribute("rank", "source", "source"); //Make edges int edgeCounter = 0; foreach (Output output in outputToNode.Keys) { if (!output.IsConnectedToAnything()) { continue; } var from = outputToNode[output]; foreach (var input in output.GetConnectedInputs()) { if (input.Node != null && input.Node is INoPlaceAndRoute) { continue; } if (!inputToNode.ContainsKey(input)) { continue; } var to = inputToNode[input]; var edge = graph.GetOrAddEdge(from, to, (edgeCounter++).ToString()); edge.SafeSetAttribute("tailport", outputToPort[output], " "); edge.SafeSetAttribute("headport", inputToPort[input], " "); } } graph.ComputeLayout(); Dictionary <FIRRTLNode, Rectangle> firNodeRects = new Dictionary <FIRRTLNode, Rectangle>(); foreach (var firToVizNode in firNodeToNode) { var centerF = firToVizNode.Value.Position(); var center = new Point((int)(centerF.X * dpi), (int)(centerF.Y * dpi)) / ppi; var nodeSize = nodeSizes[firToVizNode.Key]; var topLeft = center - (nodeSize / 2); firNodeRects.Add(firToVizNode.Key, new Rectangle(topLeft, nodeSize)); } graph.FreeLayout(); Point min = new Point(int.MaxValue, int.MaxValue); foreach (var rect in firNodeRects.Values) { min = Point.Min(min, rect.Pos); } Point offsetBy = min.Abs(); foreach (var firRect in firNodeRects) { placments.AddNodePlacement(firRect.Key, new Rectangle(offsetBy + firRect.Value.Pos, firRect.Value.Size)); } Point borderPadding = new Point(100, 200); placments.AutoSpacePlacementRanks(mod); placments.AddBorderPadding(borderPadding); return(placments); }