From: erg Date: Thu, 24 Aug 2006 21:27:52 +0000 (+0000) Subject: Update libguide to reflect plug-in architecture X-Git-Tag: LAST_LIBGRAPH~32^2~5946 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=98a56fb8f862297727ac82671f990ca44a27cfa9;p=graphviz Update libguide to reflect plug-in architecture --- diff --git a/doc/libguide/samples.tex b/doc/libguide/samples.tex index a50c1e32c..da4e7fcc2 100644 --- a/doc/libguide/samples.tex +++ b/doc/libguide/samples.tex @@ -1,43 +1,39 @@ \section{A sample program: {\tt simple.c}} \label{sec:simple} -This following code illustrates an application which only uses \gviz\ to -position a graph. The application will either provide its own -rendering, perhaps in an interactive setting, or, as in this -case, it does not render the graph at all. +This following code illustrates an application which uses \gviz\ to +position a graph using the \dot\ layout and then write the output +using the {\tt plain} format. +An application can replace the call to {\tt gvRender} with its own +function for rendering the graph, using the layout information +encoded in the graph structure (cf. Section~\ref{sec:layout_info}). \pagebreak[4] \begin{verbatim} -#include +#include -int main(int argc, char** argv) +int main(int argc, char **argv) { - Agraph_t* g; - Agnode_t* n; - FILE* fp; - char buf[BUFSIZ]; - point p; + GVC_t *gvc; + graph_t *g; + FILE *fp; - aginit (); + gvc = gvContext(); if (argc > 1) - fp = fopen (argv[1],"r"); - else + fp = fopen(argv[1], "r"); + else fp = stdin; - g = agread (fp); + g = agread(fp); - dot_layout(g); - - agnodeattr(g, "pos", ""); - for (n = agfstnode(g); n; n = agnxtnode(g,n)) { - p = ND_coord_i(n); - sprintf(buf,"%d,%d", p.x, p.y); - agset(n,"pos",buf); - printf ("node %s at position (%s)\n", n->name, buf); - } - agwrite(g, stdout); + gvLayout(gvc, g, "dot"); - dot_cleanup(g); - return 0; -} + gvRender(gvc, g, "plain", stdout); + + gvFreeLayout(gvc, g); + + agclose(g); + + return (gvFreeContext(gvc)); +} \end{verbatim} \section{A sample program: {\tt dot.c}} @@ -49,37 +45,29 @@ is precisely how the \dot\ program is written, ignoring some signal handling, its specific declaration of the {\tt Info} data (cf. Section~\ref{sec:info}), and a few other minor details. -If someone desired to write a new layout algorithm, this -code could be copied directly, merely changing the algorithm-specific -layout and cleanup functions \verb+dot_layout+ and \verb+dot_cleanup+. -A more detailed and realistic example is provided in Appendix~\ref{sec:demo}. - \begin{verbatim} -#include - -int main(int argc, char** argv) -{ - Agraph_t *g, *prev=NULL; +#include + +int main(int argc, char **argv) +{ + graph_t *g, *prev = NULL; GVC_t *gvc; gvc = gvContext(); - - dotneato_initialize(gvc,argc,argv); - while ((g = next_input_graph())) { + gvParseArgs(gvc, argc, argv); + + while ((g = gvNextInputGraph(gvc))) { if (prev) { - dot_cleanup(prev); + gvFreeLayout(gvc, prev); agclose(prev); } + gvLayoutJobs(gvc, g); + gvRenderJobs(gvc, g); prev = g; - - gvBindContext(gvc, g); - - dot_layout(g); - dotneato_write(gvc); } - dotneato_terminate(gvc); - return 0; + return (gvFreeContext(gvc)); } + \end{verbatim} \section{A sample program: {\tt demo.c}} @@ -88,52 +76,44 @@ This example provides a modification of the previous example. Again it relies on the \gviz\ renderers, but now it creates the graph dynamically rather than reading the graph from a file. \begin{verbatim} -#include +#include -int main(int argc, char** argv) +int main(int argc, char **argv) { Agraph_t *g; - Agnode_t *n,*m; + Agnode_t *n, *m; Agedge_t *e; - Agsym_t *a; - GVC_t *gvc; + Agsym_t *a; + GVC_t *gvc; - /* set up renderer context */ + /* set up a graphviz context */ gvc = gvContext(); - /* Accept -T and -o options like dot. - * Input files are ignored in this demo. */ - dotneato_initialize(gvc, argc, argv); + /* parse command line args - minimally argv[0] sets layout engine */ + gvParseArgs(gvc, argc, argv); /* Create a simple digraph */ - g = agopen("g",AGDIGRAPH); - n = agnode(g,"n"); - m = agnode(g,"m"); - e = agedge(g,n,m); + g = agopen("g", AGDIGRAPH); + n = agnode(g, "n"); + m = agnode(g, "m"); + e = agedge(g, n, m); /* Set an attribute - in this case one that affects the visible rendering */ - if (!(a = agfindattr(g->proto->n, "color"))) - a = agnodeattr(g, "color", ""); - agxset(n, a->index, "red"); - - /* bind graph to GV context - currently must be done before layout */ - gvBindContext(gvc,g); + agsafeset(n, "color", "red", ""); - /* Compute a layout */ - neato_layout(g); + /* Compute a layout using layout engine from command line args */ + gvLayoutJobs(gvc, g); /* Write the graph according to -T and -o options */ - dotneato_write(gvc); + gvRenderJobs(gvc, g); - /* Clean out layout data */ - neato_cleanup(g); + /* Free layout data */ + gvFreeLayout(gvc, g); /* Free graph structures */ agclose(g); - /* Clean up output file and errors */ - dotneato_terminate(gvc); - - return 0; -} + /* close output file, free context, and return number of errors */ + return (gvFreeContext(gvc)); +} \end{verbatim} diff --git a/doc/libguide/types.tex b/doc/libguide/types.tex index 21e7ac30b..b30f8fe3a 100644 --- a/doc/libguide/types.tex +++ b/doc/libguide/types.tex @@ -1,5 +1,24 @@ -\section{String representations of types} +\section{Some basic types and their string representations} \label{sec:types} +A {\tt point} type is the structure +\begin{verbatim} +struct { + int x, y; +} +\end{verbatim} +The fields can either give an absolute position or represent a +vector displacement. +A {\tt pointf} type is the same, with {\tt int} replaced with {\tt double}. +A {\tt box} type is the structure +\begin{verbatim} +struct { + point LL, UR; +} +\end{verbatim} +representing a rectangle. The {\tt LL} gives the coordinates of the +lower-left corner, while the {\tt UR} is the upper-right corner. +A {\tt boxf} type is the same, with {\tt point} replaced with {\tt pointf}. + The following gives the accepted string representations corresponding to values of the given types. Whitespace is ignored when converting these values from strings diff --git a/doc/libguide/unconnect.tex b/doc/libguide/unconnect.tex index bbe13758c..f475499b9 100644 --- a/doc/libguide/unconnect.tex +++ b/doc/libguide/unconnect.tex @@ -5,7 +5,7 @@ Each is then extended to handle the not uncommon case of having multiple components. Most of the time, the obvious approach is used: draw each component separately and then assemble the drawings into a single layout. The only place this is not done is in \neato\ when the mode is -{\tt MODE\_KK} and {\tt pack} is false (cf. Section~\ref{sec:neato}). +{\tt "KK"} and {\tt pack="false"} (cf. Section~\ref{sec:neato}). For the \dot\ algorithm, its layered drawings make the merging simple: the nodes on the highest rank of each component are all put @@ -17,47 +17,34 @@ with unconnected graphs, especially by supplying a technique for packing arbitrary graph drawings together quickly, aesthetically and with efficient use of space. The following code indicates how the library can be integrated with the -basic algorithm (cf. Section~\ref{sec:twopi}). +basic layout algorithms given an input graph {\tt g} and a \gvc\ value +{\tt gvc}. \begin{verbatim} - Agnode_t* c = NULL; - Agraph_t** ccs; /* array of connected components */ - Agraph_t* sg; - Agnode_t* c = NULL; - int ncc; /* number of connected components */ - int i; + graph_t *sg; + FILE *fp; + graph_t** cc; + int i, ncc; - ccs = ccomps (g, &ncc, (char*)0); - if (ncc == 1) { - circleLayout (g,ctr); - adjustNodes (g); - spline_edges(g); - } - else { - pack_info pinfo; - pack_mode pmode = getPackMode (g,l_node); + cc = ccomps(g, &ncc, (char*)0); - for (i = 0; i < ncc; i++) { - sg = ccs[i]; - if (ctr && agcontains (sg, ctr)) c = ctr; - else c = 0; + for (i = 0; i < ncc; i++) { + sg = cc[i]; nodeInduce (sg); - circleLayout (sg,c); - adjustNodes (sg); - } - spline_edges(g); - pinfo.margin = getPack (g, CL_OFFSET, CL_OFFSET); - pinfo.doSplines = 1; - pinfo.mode = pmode; - pinfo.fixed = 0; - packSubgraphs (ncc, ccs, g, &pinfo); + gvLayout(gvc, sg, "neato"); } + pack_graph (ncc, cc, g, 0); + + gvRender(gvc, g, "ps", stdout); + for (i = 0; i < ncc; i++) { - agdelete (g, ccs[i]); + sg = cc[i]; + gvFreeLayout(gvc, sg); + agdelete(g, sg); } \end{verbatim} -The call to {\tt ccomps} splits the graph into its connected +The call to {\tt ccomps} splits the graph {\tt g} into its connected components. {\tt ncc} is set to the number of components. The components are represented by subgraphs of the input graph, and are stored in the returned array. The function gives names to the components @@ -78,48 +65,30 @@ to to true if the graph has nodes with fixed positions. In this case, the component containing these nodes is the first one in the returned array. Continuing with the example, -if there is only one component, we can revert to the base implementation -for simplicity. If there are multiple components, -we take one at a time, using {\tt nodeInduce} to create the -corresponding node-induced subgraph, laying out the component with -{\tt circleLayout}, and removing node overlaps, if necessary, by -calling {\tt adjustNodes}. As a technical point, if a center node -is provided, it is only used with the component containing it. A -better implementation would allow a center node to be specified on -a per component basis. - -After the nodes of each component are positioned, with coordinates -stored in the attributes {\tt pos[0]} and {\tt pos[1]}, the code -calls {\tt spline\_edges} to generate the edge representations. - -Next, it uses the \pack\ function {\tt packSubgraphs} to reassemble +we take one component at a time, use {\tt nodeInduce} to create the +corresponding node-induced subgraph, and then lay out the component +with {\tt gvLayout}. Here, we use \neato\ for each layout, but it +is possible to use a different layout for each component.\footnote{ +At present, the \dot\ layout has a limitation that it only works on +a root graph. Thus, to use \dot\ for a component, one needs to create +a new copy of the subgraph, apply \dot\, and then copy the position +attributes back to the component.} + +Next, we use the \pack\ function {\tt pack\_graph} to reassemble the graph into a single drawing. To position the components, \pack\ uses the polyomino-based approach described by Freivalds et al\cite{pack}. The first three arguments to the -function are clear. The fourth argument sets various parameters -used in the packing. The {\tt pinfo.margin} field specifies the -margin, in points, maintained between components. The value -{\tt CL\_OFFSET} is defined by \gviz; it is the same amount of -space used by \dot\ around clusters. Here, we employ the auxiliary -function {\tt getPack}. This uses the graph attribute {\tt "pack"} to -determine this value, which can take boolean and integer values. -If the attribute evaluates to a non-negative integer, this value is -returned. If the attribute evaluates to true, the third argument is -returned. Otherwise, the function returns the second value. - -The {\tt pinfo.doSplines} field, if non-zero, -tells the function that edge representations have already been -computed for the graph and should be used in determining the packing. -Otherwise, the packing will treat edges as line segments connecting -the centers of the two endpoints. +function are clear. The fourth argument indicates whether or not +there are fixed components. -The {\tt pinfo.mode} field specifies how the packing should be done. At +The {\tt pack\_graph} function uses the graph's {\tt packmode} +attribute to determine how the packing should be done. At present, packing uses the single algorithm mentioned above, but allows -three varying granularities, represented by the values {\tt l\_node}, -{\tt l\_clust} and {\tt l\_graph}. In the first case, packing is done at +three varying granularities, represented by the values {\tt "node"}, +{\tt "clust"} and {\tt "graph"}. In the first case, packing is done at the node and edge level. This provides the tightest packing, using the least area, but also allows a node of one component to lie between -two nodes of another component. The second value, {\tt l\_clust}, +two nodes of another component. The second value, {\tt "clust"}, requires that the packing treat top-level clusters with a set bounding box {\tt GD\_bb} value like a large node. Nodes and edges not entirely contained within a cluster are handled as in the previous @@ -129,26 +98,9 @@ last case does the packing at the graph granularity. Each component is treated as one large node, whose size is determined by its bounding box. -In our example, we use another library function, {\tt getPackMode}, -to set the mode value. This function uses the graph's {\tt "packmode"} -attribute to determine the value. If this is {\tt "node"}, -{\tt "cluster"} or {\tt "graph"}, the function returns -{\tt l\_node}, {\tt l\_clust} and {\tt l\_graph}, respectively. -Otherwise, the function returns its second argument. - -The last field, {\tt pinfo.fixed}, is used to constrain the placement -of certain components. If non-NULL, this field should point to an -array of {\tt ncc} booleans, where {\tt pinfo.fixed[i]} is true if -component {\tt i} should be left at its current position. If the -application specifies fixed components, these are placed first. Then -the remaining components are packed into the unoccupied space, respecting -the {\tt pinfo.mode} field. It is the application's responsibility to -make sure that the fixed components do not overlap each other, if that -is desired. - Note that the library automatically computes the bounding box of each of the components. Also, -as a side-effect, {\tt packSubgraphs} finishes by recomputing and +as a side-effect, {\tt pack\_graph} finishes by recomputing and setting the bounding box attribute {\tt GD\_bb} of the graph. The final step is to free the component subgraphs.