Author: Gordon Woodhull
Description: dynagraph graph layout server

# Dynagraph

Dynagraph is a platform-independent graph layout engine. There are three ways to connect with the dynagraph layout engines:

- To generate consecutive drawings, dynagraph can be used as a batch command.
- Dynagraph can be connected to a client over a pipe interface where both use a language called incrface to communicate changes.
- Clients can static-link to the Dynagraph libraries and access the C++ API directly.

# dg command line options

The executable dg can be run in two modes:

- If no source is specified with either the -i or -c commands, the program goes into incrface mode.
- When a source is specified, dg reads from a dot file or creates a random graph, traverses the graph according to -t, and generates output dot files (if -ol specified).

-d use dot-compatible coordinates
  Dynagraph internally uses dimensionless coordinates, but traditionally dot used points for coordinates and inches for node sizes. The node width, height, and textsize attributes will be multiplied by 72. This also affects default attributes. The default resolution is 1 x 1 instead of 0.1 x 0.1; the default separation is 0.375 x 0.5" instead of 0.5 x 1; the default minimum width is 0.75" and height 0.5" instead of 1 and 1.
-ifilename specify input dot file
  This can be used to specify a file for batch layout, or in combination with the -t option.
-tm choose traversal method
  Inserts the graph specified with -i bit by bit using one the following methods:

  a | All at once (batch). Equivalent to not specifying -t.
  n | Insert all nodes, then all edges.
  b | Breadth first search.
  d | Depth first search.
  r | Randomly insert edges.
  w | "Wandering" (not recommended).

-cN create edges randomly
  Alternative to -i and -t for testing/fun. Draws a random connected graph. One edge per step, N steps; 95% of edges leaves.
-raN report on a to stream N
  Dynagraph can write logs to up to 10 files (output filenames specified with -o). a specifies one or more report types from the following:

  c | Crossing optimization
  t | Time usage breakdown
  d | Dynadag tallies
  g | Dump graph in dotfile format after every step
  q | Dump input queue before each step
  r | Output readability statistics
  s | Output stability statistics
  p | Report on progress
  b | Bug of the day: used for random debugging

-oNfilename write stream N to filename
  Dynagraph will write all reports assigned to N to the file filename. See -r.
-olfilename
  output the layout at each step to filename1.dot, filename2.dot, etc.
-f force relayout
  Dynagraph will use the traversal method specified with -t but will redo the layout from scratch on each step.

# Command interface - incrface

In this interface, the client and server communicate requests and modifications over two pipes using a command language. The language is the same for client requests and server events, with the exception that the server does not use the "segue" command. Many commands can accept attributes in the same syntax favored by dot, e.g. [attr=value,attr2="value with spaces or other parser-confusing stuff"]

open view V [attrs]
  Opens a view named V. The optional attributes apply to the layout or the graph.
modify view V [attrs]
  Applies the attributes to V
close view V
  Closes view V.
insert V node N [attrs]
  Creates a node named N in view V.
insert V edge E [attrs]
  Creates an edge named E in view V.
modify V node N [attrs]
  Applies the attributes to N.
modify V edge E [attrs]
  Applies the attributes to E.
delete V node N
  Removes N from the layout.
delete V edge E
  Removes E from the layout.
lock view V
  Increments the lock count on V. While the count is greater than zero, dynagraph will not do any layouts and will not output. In addition, dynagraph uses lock and unlock to mark a group of changes so that the client can respond more efficiently.
unlock view V
  Decrements the lock count on V. If the count returns to zero, does a new layout based on all queued changes.
segue view V graph
  Graph specifies a full graph in dotfile format; dynagraph will make all necessary changes to transform the current graph to that specified.

# String attributes understood/emitted by dynagraph

Attributes are input attributes unless otherwise specified.

## Graph attributes

resolution = "x,y"
  (in) See GraphGeom::resolution in the API reference.
separation = "x,y"
  (in) See GraphGeom::separation in the API reference.
bb = "l,b,r,t"
  (out) Specifies the bounding box of the current layout. Corresponds to GraphGeom::bounds

## Node attributes

shape
  Specifies the name of the base shape, which will select values for the other parameters which can be overridden. Default: ellipse.

  ellipse | The base shape is a Bezier spline approximation of an ellipse.
  polygon | The base shape is a polygon.

regular = true|false
  If true, specifies that the aspect ratio of the shape will be 1:1. Default: false.
sides
  The number of sides of the polygon. Not applicable for Bezier spline-based shapes. Default: 4.
peripheries
  The number of extra borders to draw around the shape. Default: 0.
perispacing
  The distance between the parallel lines of the peripheries. Default: 0.
rotation
  The angle, in radians, that the shape should be turned from one in which the bottom is flat. Default: 0.
skew
  The amount to tilt the shape. Default: 0.
distortion
  Make the top bigger than the bottom. Default: 0.
textsize = "x,y"
  The size of the text to fit within this shape. For consistency heights with different line lengths, the shape will be stretched to fit a square whose size is the smaller of x and y, and then stretched again to fit the larger.
width
  minimum external width
height
  minimum external height
boundary
  specifies a polyline boundary as a list of coordinates
pos = "x,y"
  (in,out) Specifies the position coordinate of the node. This is the offset for the polys and boundary parameters. See NodeGeom::pos.
polys = "bB x1,y1 x2,y2 ...; bB x,y..."
  (out) Specifies the shapes generated for this node. Shapes are separated with semicolons. B specifies the Bezier spline level for the current shape.

## Edge attributes

pos = "x1,y1 x2,y2 ..."
  (in,out) Specifies the curve that this edge follows.

# Static linking to Dynagraph

The Dynagraph library can be statically linked to a C++ program. The API for connecting with a Dynagraph layout server is defined in common/Dynagraph.h and auxiliary headers. At present there is only one implemented layout server, DynaDAG, but the same API (with some adaptation) will be used for other engines in the future.

## Getting started

To use dynagraph from a C++ program, follow these steps:

1. #include the header for the particular layout server you with to use (e.g. DynaDAG.h)
2. For each layout, you will need two graphs to hold the data, a ChangeQueue to track changes, and an instance of the layout server. Create two instances of the Layout class defined in Dynagraph.h. This class is a graph which holds information about the layout preferences and geometry. We will call these graphs working and current; working is where we create new elements, and current is the subgraph which represents what is showing in the layout. Make current a subgraph of layout by specifying layout in the constructor call. e.g.:

   `Layout layout,current(&layout);`

3. Since dynagraph is resolution-independent, you must set the resolution, separation and label separation as appropriate. Use the gd<> function to access data in a graph by its type. Since these parameters are in the GraphGeom portion of the graph data, prepation for centimeter coordinates might look like:

   `gd<GraphGeom>(&layout).resolution = Coord(0.1,0.1);
   gd<GraphGeom>(&layout).separation = Coord(0.5,1.0);
   gd<GraphGeom>(&layout).labelSep = Coord(0.5,0.5);`

4. Create an instance of the ChangeQueue class, which will hold changes we are making as well as ones made by the layout server. This needs to know both the working and current graphs:

   `ChangeQueue queue(&working,&current);`

5. Create an instance of the layout engine, passing it the current layout, e.g.:

   `DynaDAGServer server(&current);`

6. If you are using DynaDAG you will need to specify the crossing optimization method (this is obscure and should go away):

   `Optimizer *optim = new HybridOptimizer2(server.config); server.optChooser.choices.push_back(make_pair(0,optim));`

7. Now you are ready to start drawing a graph. Here's how to insert a new node at (50,50):

   `Layout::Node *n = working.create_node();
   gd<NodeGeom>(n).pos = Coord(50,50);
   queue.InsNode(n);`

   Or to move a node, similarly:

   `gd<NodeGeom>(n).pos = Coord(200,200);
   queue.ModNode(n,DG_UPD_MOVE);`

   To get the server to respond to your changes:

   `server.Process(queue)`

   Now you will need to read the queue to see what changes have been made. The queue holds six subgraphs of the working graph, to keep track of inserted nodes, inserted edges, modified nodes, modified edges, deleted nodes, and deleted edges. Ever time you call server.Process you should read all of these subgraphs and reflect the changes in your display, e.g.:

   `Layout::node_iter ni;
   for(ni = Q.modN.nodes().begin(); ni!=Q.modN.nodes().end(); ++ni)
   ;// move graphical object associated with *ni to gd<NodeGeom>(*ni).pos;`

## API Reference

### Layout graph attributes

The heart of the API is the Layout graph type. Dynagraph uses a templated graph library called LGraph, which allows the graph, nodes, and edges to be annotated with arbitrary classes. Layout is an instantiation of the LGraph class with annotations for describing the geometry and how the layout should be done.

#### GraphGeom - layout graph parameters

Bounds bounds
  (out) The bounds of the current layout. Corresponds to the bb string attribute.
Coord labelGap
  (in) The amount of space to leave between labels and nodes, e.g. if a label is on the right of a node, label.left = node.right+labelGap.x
Coord resolution
  (in) The smallest increment to recognize in the internal model. For example, use (1,1) to use integer coordinates coordinates. Corresponds to the resolution string attribute.
Coord separation
  (in) The amount of separation to leave between elements of the layout. In dynadag, x specifies the horizontal gap left between nodes and/or edges, and y specifies the displacement between nodes made for an edge of length 1. Corresponds to the separation string attribute.
SpliningLevel splineLevel
  (in) (dynadag specific) The amount of processing to do on edge splines:

  DG_SPLINELEVEL_VNODE | Draw the straight lines between nodes in the internal model.
  DG_SPLINELEVEL_BOUNDS | Draw the bounds along the edge routes that will be used for the spline.
  DG_SPLINELEVEL_SHORTEST | Draw the shortest straight-line paths within the bounds.
  DG_SPLINELEVEL_SPLINE | Draw edges with Bezier curves (default).

float timeLimit
  The number of seconds to spend on one layout step. Not yet implemented in dynadag

#### NodeGeom - node layout parameters

Position pos
  (in,out) The position of the node. If the client leaves pos.valid==false, then the node has no starting position.
Region *region
  (in) The boundary of the node, specified in terms of a rectangle and a hit-testing functions. The basic version of Region hit-tests for the rectangle; the descendant PolylineRegion hit-tests for a polyspline-bounded shape.
NailType nail
  (in) Specifies the mobility of the node:

  DG_NONAIL | The node can be positioned at the server's discretion.
  DG_NAIL_X | The server attempts to keep the node at the same X position.
  DG_NAIL_Y | The Y position (rank) is fixed.
  DG_NAIL_BOTH | The node is immobile.

#### EdgeGeom - edge layout parameters

double width
  (in) The width of the edge.
double lengthHint
  (in) The minimum length of the edge. In dynadag, this is multiplied by GraphGeom::separation.y to determine the verticle displacement between the nodes at either end of this edge.
double cost
  (in) The cost of the edge. Not used in dynadag.
Line pos
  (in,out) The client can set the initial position of the edge by filling this in and setting manualRoute==true. The server returns the edge route in this field.
bool constraint
  (in,out) In dynadag, if this flag is set to true, the edge will always point downward. If this flag is set to false, the edge can point upward when there is a cycle in the graph. Dynadag will set constraint==false if it finds a cycle while inserting the edge.
bool manualRoute
  If set to true, the server will use the value of pos as the initial position of the edge.
Port tailPort,headPort;
  Offsets of the ends of the edge from the tail and head nodes respectively.
bool tailClipped,headClipped
  Whether to clip this edge to the tail and head node regions.

### The Change Queue

The ChangeQueue holds the changes requested by the client and fulfilled by the server. The ChangeQueue is a way for client and server to list the changes they have made.

Layout *const client
  The client's working graph, which is a supergraph of what is actually in the layout.
Layout *const current
  The current layout graph, a subgraph of the client graph.
Layout insN,modN,delN,insE,modE,delE
  Subgraphs of the client graph which list the nodes and edges changed. When client and current are synchronized, these are empty.
void InsNode(Layout::Node *n)
void InsEdge(Layout::Edge *e)
  Marks an object created with client->create_node or client->create_edge by inserting it into the insN or insE subgraph.
void ModNode(Layout::Node *n,Update u)
void ModEdge(Layout::Edge *e,Update u)
  Marks an object whose properties have changed by inserting it into the modN or modE subgraph, and ORing the Update flags into the igd<Update> portion of the object's data.
void DelNode(Layout::Node *n)
void DelEdge(Layout::Edge *e)
  Marks an object to be deleted by inserting it into the delN or delE subgraph. The object will not actually be deleted until ChangeQueue::Okay is called.
void UpdateCurrent()
  Called by server to the current layout graph current based on the changes marked.
void Okay(bool doDelete = false)
  Called by the client after server processing to execute deletions and clear the change subgraphs.
bool Empty()
  Returns true if the change subgraphs are empty.
ChangeQueue &operator=(ChangeQueue &Q)
  Assigns the contents of another ChangeQueue to this one.
ChangeQueue &operator+=(ChangeQueue &Q)
  Adds the contents of another ChangeQueue to this one.