I built an initial version of a new control which allows to draw graphviz graphs with Nitrogen and added it to the collection of Nitrogen_Elements.
As far as I know Erlang ecosystem doesn’t have a convenient library to draw dot files with graphviz. Graphviz is a system which allows to describe arbitrary complex graphs in dot format, calculate a layout from the dot files and output this layout in a number of formats (PDF, XML, SVG and Postscript). Graphviz is a very large C++ library.
The control is called element_viz and it is a wrapper for two very interesting libraries:
- Viz.js – is Emscripten generated javascript library from Graphviz C++ code base. It allows to calculate a graph layout on a browser side and display a graph in SVG format on html page
- jquery.graphviz is a lib which allows to move (click and drag and drop) and remove (double click on a node or on a edge) the nodes and the edges of a graph created with graphviz
The control itself is extremely simple as all the magic is happening in Viz.js.
-module(element_viz). -include ("nitrogen_elements.hrl"). -include_lib("nitrogen_core/include/wf.hrl"). -compile(export_all). reflect() -> record_info(fields, viz). render_element(Record) -> ID = Record#viz.id, %% get data, should be binary string in dot format Data = Record#viz.data, %% generate graph html and append to panel placeholder wf:wire(ID, wf:f("$(function(){jQuery(obj('~s')).html(Viz('~s', \"svg\"));})", [ID, Data])), %% set svg width 100% to fill panel wf:wire(ID, wf:f("$(function(){jQuery(obj('~s')).find(\"svg\").width('100%');})", [ID])), %% add jquery.graphviz wf:wire(ID, wf:f("$(function(){jQuery(obj('~s')).find(\"svg\").graphviz({status: true});})", [ID])), %% create html markup #panel{id = ID}. %% not used yet event(Event) ->
Control use is also very simple – add #viz{} record and pass a binary string with dot representation of the graph.
-define(DATA, <<"digraph G {subgraph cluster_0 {style=filled;color=lightgrey; node [style=filled,color=white]; a0 -> a1 -> a2 -> a3;label = \"process #1\";} subgraph cluster_1 {node [style=filled]; b0 -> b1 -> b2 -> b3; label = \"process #2\";color=blue} start -> a0; start -> b0; a1 -> b3; b2 -> a3; a3 -> a0; a3 -> end; b3 -> end; start [shape=Mdiamond]; end [shape=Msquare];}">>). body(_Tag) -; %% output html markup [ #viz{id = viz, data = ?DATA} ].
The final result on the page looks like this:
And you can move the nodes and edges of this graph – this functionality is provided by jquery.graphviz.
This control might be very useful and could be extended further with Websockets, so the changes to the graph could be pushed from the server side in dot format and reflected dynamically on the page.
See the full example here