-
Notifications
You must be signed in to change notification settings - Fork 32
Visualization with GraphVIZ DOT
AutomataLib provides a very convenient way of visualizing automata and graphs, using the well-known GraphVIZ DOT tool. Visualization here means both exporting them to a DOT file, which can be processed for rendering by external tools (such as those distributed with GraphVIZ), and also rendering and displaying them directly from your application using a Java interface. This page will guide you on how to accomplish this.
Impatient readers interested in application only may skip right to the example.
In order to use DOT for visualizing automata and graphs, the GraphVIZ software has to be installed on your machine (downloads page). Additionally, the dot
(dot.exe
on Windows) utility has to be installed in a directory contained in the PATH
environment variable.
The central interface for customizing how an automaton or graph is rendered is GraphDOTHelper
(source, javadoc) from the automata-core artifact. This interface declares four methods:
-
writePreamble()
can be used to write arbitrary GraphVIZ statements before the actual body of the graph is written (but after the openingdigraph {
). This could, for example, include additional nodes. -
writePostamble()
can be used to write arbitrary GraphVIZ statements after the actual body of the graph has been written. In this method, nodes (states) from the graph (automaton) can be referenced by a mapping providing their ID, which is not possible in thewritePreamble
method. -
getStateProperties()
allows to define GraphVIZ properties for single nodes (states), or signal to suppress rendering them by returningfalse
. For example, invokingproperties.put(SHAPE, "box")
will lead to the current node being rendered as a rectangle instead of a circle. -
getEdgeProperties()
allows to define GraphVIZ properties for single edges (transitions), or signal to suppress rendering them by returningfalse
. For example, invokingproperties.put("style", "dashed")
will lead to the respective edge being drawn using a dashed line.
A graph can be annotated with information about how it prefers to be rendered. This is accomplished by implementing the DOTPlottableGraph
(source, javadoc) interface, declared in the automata-core artifact. This interface extends the Graph
interface by declaring one additional method, getGraphDOTHelper()
, which is used to obtain a GraphDOTHelper
for rendering this graph.
Transforming an Automaton
or Graph
object to a DOT description is handled by the GraphDOT
(source, javadoc) utility class. The procedure slightly differs, depending on whether an Automaton
or a Graph
should be rendered, and whether or not this comes with instructions how it prefers to be rendered.
All of the methods mentioned below take a parameter of type Appendable
, to which the DOT description is written. Appendable
is a very lean interface, implemented by most of the Java framework classes appropriate for this goal, like StringBuilder
, BufferedWriter
and so on.
As the name "GraphVIZ" suggests, the DOT format describes graphs, not automata. Rendering Graph
s therefore is the much easier case.
-
write(DOTPlottableGraph<N,E> graph, Appendable a, ...)
is the straightforward case, where aDOTPlottableGraph
providing its ownGraphDOTHelper
is translated to DOT. -
write(Graph<N,E> graph, Appendable a, ...)
works for everyGraph
reference. This method employs reflection to check if theGraph
object passed is an instance of aDOTPlottableGraph
. If so, the respective DOT helper will be used; otherwise, a standard helper will be used. -
write(Graph<N,E> graph, GraphDOTHelper<N,? super E> helper, Appendable a, ...)
is used to explicitly specify aGraphDOTHelper
. Note: ForDOTPlottableGraph
s, the shipped helper is ignored.
As stated above, the DOT format describes graphs. Rendering automata is accomplished by internally translating them to a Graph
representation, if necessary. Note that since the Automaton
interfaces provides no explicit input alphabet, the inputs to be considered when rendering have to be manually provided.
-
write(Automaton<S,I,T> automaton, GraphDOTHelper<S,? super Pair<I,T>> helper, Collection<? extends I> inputs, Appendable a, ...)
renders an automaton using the suppliedGraphDOTHelper
. The automaton is translated into a graph as described [here](Automata to Graph Conversion). -
write(Automaton<S,I,T> automaton, Collection<? extends I> inputs, Appendable a, ...)
renders an automaton using either a specialized helper (obtained from automata implementing theDOTPlottableAutomaton
(source, javadoc) interface), or a default helper for rendering automata. The automaton is translated into a graph as described [here](Automata to Graph Conversion).
You might have noticed the ellipsis (...
) in most of the methods mentioned above. In fact, all those method come with an additional varargs parameter called additionalHelpers
of type GraphDOTHelper<N,? super E>[]
. These parameter can be used to specify a sequence of GraphDOTHelper
s for further customization. GraphVIZ properties for nodes and edges are aggregated by subsequently calling getXXXProperties()
on each helper (in the order they are being passed). If one of these invocations return false
, the respective node or edge is not rendered. The writePreamble
and writePostamble
methods are handled in a similar fashion.
Note that while properties set by previous helpers may be overridden or even completely removed by invoking properties.clear()
, it is not possible for any of the additional helpers to revert a possible rendering suppression by a previous (including the builtin, if the graph is a DOTPlottableGraph
).
The class DOT
(source, javadoc) from the automata-commons-dotutil artifact provides several methods that facilitate interaction with the external dot
program. However, at this point we just want to focus on the most convenient way: by creating a special Writer
object (note that Writer
as a subclass of Appendable
can be passed to the GraphDOT.write(...)
methods mentioned above).
This Writer
can be used to write arbitrary GraphVIZ data to. Its close()
method, however, triggers the actual DOT rendering to be performed and opens a window displaying the rendering result (or a message box, if there were errors during rendering).
The Writer
can be instantiated by a call to DOT.createDotWriter(boolean modal)
. The mandatory boolean
parameter indicates whether or not the window shall be modal or not. In the former case, the call to close()
will block, and execution (in the calling thread) will not proceed until the window is closed.
The following listing shows how a simple plottable graph can be visualized in a modal window.
DOTPlottableGraph<?,?> graph = ...;
Writer w = DOT.createDOTWriter(true); // true indicates that the dialog is modal
GraphDOT.write(graph, w); // Requires handling of IOException!
w.close(); // Causes the visualization window to appear. Note that since the dialog
// was set to be modal, this call will block until the window is closed.