Design 

Proxy userdata

The basic idea  of the design is to make graphs look like Lua objects. As a consequence the usage of the "dot" language is not required, but is available. Graphs can be constructed, layed out and rendered from a Lua script without the need for external programs.

In order to achieve this goal LuaGRAPH operates as an access or proxy layer to the graphviz libraries' functions,  objects and object  attributes.

Graph objects like graphs, nodes and edges are stored in directories maintained by the graphviz library and are represented as Lua userdata objects to the programmer. The userdata objects function as a proxy to the graph objects mainly to access their attributes. The userdata  is created and deleted on the fly whenever needed without necessarily creating or deleting graph objects. 

The proxy objects are stored in the Lua registry. For instance, reading a graph from a file creates a graph object and all graphviz objects but only a proxy for the main graph. By subsequently referencing graph objects by there name or by iterators the corresponding userdata objects are automatically created.

Attributes

Attribute access is implemented using metamethods of Lua userdata. All standard attributes are strings, numbers are automatically converted to strings, but not vice versa. Attributes are transparently stored by LuaGRAPH as key-value pairs in the graphviz directories and not in Lua data structures. LuaGRAPH does typically not interprete the key of an attribute. Hence, any key-value pair can be stored. LuaGRAPH has no explicit knowledge about the attributes, which makes it insensitive against future versions of the graphviz library with potentially new attributes. On the other hand any key not known by the graphviz library doesn't hurt and can be regarded as user defined object attributes. 

In order to allow handling of such arbitrary typed attributes, LuaGRAPH maintains an internal attribute key .attrib, which allows LuaGRAPH to attach data of any type (strings, numbers, tables, boolean, other userdata and functions) to a graph object. This allows the programmer to define it's own methods for a graph object. 

Object attributes are accessed in the common dot notation for tables with strings as keys, e.g. if n is a proxy reference to a node, it's drawing color can be set this way: n.color = "blue".

Object Access

Graph objects are typically accessed via methods of other graph objects, e.g. a node can be created or retrieved by a graph's g.node() method:  

n = g.node("tail").  

An edge can be created using the n.edge() method of the tail node and providing a reference to the head node as parameter to this method:  

e = n:edge("head")

Iterators

LuaGRAPH implements a variety of iterators to iterate over subgraphs of a graph, nodes and edges of a graph or edges of a node. See the LuaGRAPH Reference section for details.

Higher Abstraction Layers

LuaGRAPH provides some means of higher abstraction to create more sophisticated graph objects like clusters and records. While clusters are just subgraphs with a name extension according to graphviz' convention, a record requires a special syntax in it's label attribute. In order to help to construct this syntax LuaGRAPH provides a means using collections of horizontal and vertical box constructors to define such a record. 

Using tables to describe a graph

LuaGRAPH provides a number of functions that return a constructor function for  graph object creation. A hierarchical collection of such constructors allows the programmer to define a graph using Lua's table syntax. Here is an example:

gr = require "graph"

--
-- For simple formatted printing
--
local function printf(fmt, ...)
print(string.format(fmt, unpack(arg)))
end

--
-- Get a local reference for frequently used functions (optional)
--
local node, edge, subgraph, cluster, digraph, strictdigraph =
gr.node, gr.edge, gr.subgraph, gr.cluster, gr.digraph, gr.strictdigraph

--
-- Example graph
--
local g = digraph{"G",
cluster{"c0",
edge{"a0", "a1", "a2", "a3"}
},
cluster{"c1",
edge{"b0", "b1", "b2", "b3"}
},
edge{"x", "a0"},
edge{"x", "b0"},
edge{"a1", "a3"},
edge{"a3", "a0"}
}

--
-- Same graph in dot notation
--
local dotsource = [[
digraph G {
subgraph cluster_c0 {a0 -> a1 -> a2 -> a3;}
subgraph cluster_c1 {b0 -> b1 -> b2 -> b3;}
x -> a0;
x -> b0;
a1 -> a3;
a3 -> a0;
}
]]


-- Print graph as dotfile
printf("1. Generated 'dot' format:")
g:write()
printf("2. Source in 'dot' format:\n %s\n", dotsource)

-- Show the graph
g:showdotty()

-- Close the graph
g:close()