Convert facts to GraphViz dot file using DCGs

This example makes use of the persistence file created in this post and converts the facts into a GraphViz dot file. Using GraphViz the dot file can be converted to a PDF, SVG, etc.

Typically DCGs are demonstrated by parsing text into a structure, this does the opposite and converts structures into text.

:- module(module_graph,[
        write_graph/1
    ]).


:- use_module(library(persistency)).
:- use_module(library(fileutils)).

:- set_prolog_flag(double_quotes, codes).

:- persistent
    uses(super:atom,module:atom),
    imports(module:atom,import:atom).

:- initialization(db_attach('module.journal', [])).

write_graph(Filepath) :-
    graph(Graph),
    with_output_to_file(Filepath,write(Graph)).

nodes(Nodes) :-
    setof(Super,uses(Super,_),A),
    setof(Module,uses(_,Module),B),
    merge(A,B,Nodes), !.

edges(Edges) :-
    setof(uses(Super,Module),uses(Super,Module),Edges).

graph(Graph) :-
    phrase(graph,Codes,[]),
    string_codes(Graph,Codes).

graph -->
    {
        nodes(Nodes),
        edges(Edges)
    },
    header,
    nodes(Nodes),
    edges(Edges),
    footer.

header -->
    "digraph module_graph\n\c
    {\n\c
    	overlap=false;\n\c
    	spline=true;\n\c
    	contentrate=true;\n\c
    	node [shape=box,fontname=\"Times\"];\n\c
    	edge [arrowhead=vee];\n\c
        \n\c
    	labeljust=\"l\";\n\c
    	fontname=\"Times Bold\";".

footer -->
    "\n}".

nodes([H|T]) -->
    node(H),
    "\n",
    nodes(T).
nodes([]) --> [].

node(Name) -->
    { string_codes(Name,Codes) },
    "\"",
    Codes,
    "\"             [fontname=\"Times Bold\",style=\"bold\"];".

edges([uses(A,B)|T]) -->
    edge(A,B),
    "\n",
    edges(T).
edges([]) --> [].

edge(A,B) -->
    {
        string_codes(A,A_codes),
        string_codes(B,B_codes)
    },
    "\"",
    A_codes,
    "\"             -> \"",
    B_codes,
    "\"",
    ";".

Example usage:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.5-8-ge14460a94)

?- working_directory(_,'C:/Users/Groot/Documents').
true.

% If you created the persistent file from the other post you can skip this step.
?- ['module tools'].
true.

% If you created the persistent file from the other post you can skip this step.
?- init.
true.

?- ['module graph'].
true.

?- write_graph('module_graph.dot').
true.

To convert the dot file to an SVG. (Example is in a Windows batch file.)

SET PATH="C:\Program Files (x86)\Graphviz2.38\bin"
dot -Tsvg module_graph.dot -o module_graph.svg

Double clicking on an SVG file in Windows File Explorer should load the file into you preferred Internet browser for viewing.


The spacing of the text in the dot file could be better, but once you start using this how often will you really care about the formatting of the text in the dot file?


Also see:

http://viz-js.com/

https://js.cytoscape.org/
dot-app - Import/export of Graphviz files in Cytoscape

https://protege.stanford.edu/products.php - Protégé is a free, open-source platform that provides a growing user community with a suite of tools to construct domain models and knowledge-based applications with ontologies.


EDIT

If you want to be able to draw the graphs in an Internet browser and then drag the nodes around with a mouse try this tutorial: Getting started with Cytoscape.js

The tutorial is a bit dated.
The JavaScript can be downloaded from GitHub: https://github.com/cytoscape/cytoscape.js
Copy cytoscape.min.js from directory cytoscape.js-unstable\dist to directory getting-started

In the instructions where it notes to use

<script src='cytoscape.js'></script> 

instead use

<script src='cytoscape.min.js'></script>

Have not finished the demo but since Cytoscape.js accesses the nodes and edges as

elements: [

	// nodes
	{ data: { id: 'a', name: 'A' } },
	{ data: { id: 'b', name: 'B' } },
	{ data: { id: 'c', name: 'C' } },
	{ data: { id: 'd', name: 'D' } },
	{ data: { id: 'e', name: 'E' } },
	{ data: { id: 'f', name: 'F' } },

	// edges
	{ data: { id: 'ab', source: 'a', target: 'b'} },
	{ data: { id: 'cd', source: 'c', target: 'd'} },
	{ data: { id: 'ef', source: 'e', target: 'f'} },
	{ data: { id: 'ac', source: 'a', target: 'c'} },
	{ data: { id: 'be', source: 'b', target: 'e'} }

]

modifying the code used for GraphViz to work with Cytoscape should be easy. I plan to document this in detail and post it. :slightly_smiling_face:

3 Likes