1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Dot rendering library
21 # var graph = new DotGraph("G", "digraph")
23 # var hello = graph.add_node("hello")
24 # var world = graph.add_node("world")
26 # graph.add_edge(hello, world)
32 # Something that can be rendered in dot format.
33 abstract class DotElement
39 var attrs
= new AttributeMap
41 # Get attribute value for `key`
42 fun [](key
: String): Object do return attrs
[key
]
44 # Set attribute `value` for `key`
45 fun []=(key
: String, value
: Object) do attrs
[key
] = value
47 # Render `self` to dot format
50 res
.append
"\"{escape_id}\
" "
51 if attrs
.not_empty
then res
.append
"[{attrs.to_dot(",")}]"
52 return res
.write_to_string
55 # Return `id.escape_to_dot`
56 fun escape_id
: String do return id
.escape_to_dot
59 # Map of graph/node/edge attribute that can be rendered to dot.
61 super HashMap[String, Object]
63 # Render `self` to dot.
65 # Use `;` for graph attributes `separator` or `,` for node and edge attributes.
66 fun to_dot
(separator
: String): Text do
68 for key
, value
in self do
69 dot
.append
"{key}=\"{value.to_s}\
"{separator}"
75 # A Graph representation suited for dot format.
77 # Creating a new graph
79 # var graph = new DotGraph("G", "digraph")
80 # graph["rankdir"] = "BT"
81 # graph["ranksep"] = 0.3
82 # graph["nodesep"] = 0.3
83 # graph.nodes_attrs["fontname"] = "helvetica"
84 # graph.edges_attrs["color"] = "gray"
89 # var sub = new DotGraph("cluster_sub", "subgraph")
90 # sub["label"] = "process #1"
92 # var a0 = sub.add_node("a0")
93 # var a1 = sub.add_node("a1")
94 # sub.add_edge(a0, a1)
101 # Graph kind like `graph`, `digraph`, `subgraph`...
102 var kind
: String is writable
105 var nodes_attrs
= new AttributeMap
108 var edges_attrs
= new AttributeMap
111 var nodes
= new HashMap[String, DotElement]
113 # Add a node to the graph
115 # If the graph already contains a node with that ID, it will be replaced.
116 fun add
(element
: DotElement) do
117 nodes
[element
.id
] = element
122 # There can be multiple edges between the same couple of nodes.
123 var edges
= new Array[DotEdge]
125 # Add a new node to the graph
126 fun add_node
(id
: String): DotNode do
127 var node
= new DotNode(id
)
132 # Add an edge to the graph
133 fun add_edge
(from
, to
: DotElement): DotEdge do
134 var edge
= new DotEdge(from
, to
)
141 dot
.append
"{kind} \"{id}\
" \{\n"
142 if attrs
.not_empty
then dot
.append attrs
.to_dot
(";\n")
143 if nodes_attrs
.not_empty
then dot
.append
"node[{nodes_attrs.to_dot(",")}];\n"
144 if edges_attrs
.not_empty
then dot
.append
"edge[{edges_attrs.to_dot(",")}];\n"
145 for id
, node
in nodes
do
146 dot
.append
"{node.to_dot};\n"
149 dot
.append
("{edge.to_dot};\n")
155 # Render `self` as an SVG image
157 var proc
= new ProcessDuplex("dot", "-Tsvg")
158 var svg
= proc
.write_and_read
(to_dot
)
164 # Show dot in graphviz (blocking)
166 var f
= new ProcessWriter("dot", "-Txlib")
175 # Nodes can be created from scratch
177 # var node = new DotNode("id")
178 # node["label"] = "ID"
180 # Then added to a graph
182 # var graph = new DotGraph("G", "digraph")
185 # Or can be created directly from an existing graph
187 # var node2 = graph.add_node("id2")
188 # node2["label"] = "ID2"
194 # A dot edge that links two nodes
196 # Edges can be created from scratch
198 # var a1 = new DotNode("a1")
199 # var b1 = new DotNode("b1")
200 # var edge = new DotEdge(a1, b1)
201 # edge["color"] = "blue"
203 # Then added to a graph
205 # var graph = new DotGraph("G", "digraph")
208 # Or can be created directly from an existing graph
210 # var a2 = graph.add_node("a2")
211 # var b2 = graph.add_node("b2")
212 # var edge2 = graph.add_edge(a2, b2)
213 # edge2["color"] = "red"
219 # Node this edge is from
222 # Node this edge goes to
225 # Is this edge directed?
226 var directed
= true is writable
228 redef fun id
do return "{from.id}--{to.id}"
232 res
.append
"\"{from.escape_id}\
" "
238 res
.append
" \"{to.escape_id}\
" "
239 if attrs
.not_empty
then res
.append
"[{attrs.to_dot(",")}]"
240 return res
.write_to_string