1/* $Id$Revision: */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "patchwork.h"
15#include "adjust.h"
16#include "pack.h"
17#include "neatoprocs.h"
18
19/* the following code shamelessly copied from lib/fdpgen/layout.c
20and should be extracted and made into a common function */
21
22#define CL_CHUNK 10
23
24typedef struct {
25 graph_t **cl;
26 int sz;
27 int cnt;
28} clist_t;
29
30static void initCList(clist_t * clist)
31{
32 clist->cl = 0;
33 clist->sz = 0;
34 clist->cnt = 0;
35}
36
37/* addCluster:
38 * Append a new cluster to the list.
39 * NOTE: cl[0] is empty. The clusters are in cl[1..cnt].
40 * Normally, we increase the array when cnt == sz.
41 * The test for cnt > sz is necessary for the first time.
42 */
43static void addCluster(clist_t * clist, graph_t * subg)
44{
45 clist->cnt++;
46 if (clist->cnt >= clist->sz) {
47 clist->sz += CL_CHUNK;
48 clist->cl = RALLOC(clist->sz, clist->cl, graph_t *);
49 }
50 clist->cl[clist->cnt] = subg;
51}
52
53/* mkClusters:
54 * Attach list of immediate child clusters.
55 * NB: By convention, the indexing starts at 1.
56 * If pclist is NULL, the graph is the root graph or a cluster
57 * If pclist is non-NULL, we are recursively scanning a non-cluster
58 * subgraph for cluster children.
59 */
60static void
61mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
62{
63 graph_t* subg;
64 clist_t list;
65 clist_t* clist;
66
67 if (pclist == NULL) {
68 clist = &list;
69 initCList(clist);
70 }
71 else
72 clist = pclist;
73
74 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
75 if (!strncmp(agnameof(subg), "cluster", 7)) {
76 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);
77#ifdef FDP_GEN
78 GD_alg(subg) = (void *) NEW(gdata); /* freed in cleanup_subgs */
79 GD_ndim(subg) = GD_ndim(parent);
80 LEVEL(subg) = LEVEL(parent) + 1;
81 GPARENT(subg) = parent;
82#endif
83 addCluster(clist, subg);
84 mkClusters(subg, NULL, subg);
85 }
86 else {
87 mkClusters(subg, clist, parent);
88 }
89 }
90 if (pclist == NULL) {
91 GD_n_cluster(g) = list.cnt;
92 if (list.cnt)
93 GD_clust(g) = RALLOC(list.cnt + 1, list.cl, graph_t*);
94 }
95}
96
97static void patchwork_init_node(node_t * n)
98{
99 agset(n,"shape","box");
100 /* common_init_node_opt(n,FALSE); */
101}
102
103static void patchwork_init_edge(edge_t * e)
104{
105 agbindrec(e, "Agedgeinfo_t", sizeof(Agnodeinfo_t), TRUE); // edge custom data
106 /* common_init_edge(e); */
107}
108
109static void patchwork_init_node_edge(graph_t * g)
110{
111 node_t *n;
112 edge_t *e;
113 int i = 0;
114 rdata* alg = N_NEW(agnnodes(g), rdata);
115
116 GD_neato_nlist(g) = N_NEW(agnnodes(g) + 1, node_t *);
117 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
118 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); // node custom data
119 ND_alg(n) = alg + i;
120 GD_neato_nlist(g)[i++] = n;
121 patchwork_init_node(n);
122
123 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
124 patchwork_init_edge(e);
125 }
126 }
127}
128
129static void patchwork_init_graph(graph_t * g)
130{
131 N_shape = agattr(g, AGNODE, "shape","box");
132 setEdgeType (g, ET_LINE);
133 /* GD_ndim(g) = late_int(g,agfindattr(g,"dim"),2,2); */
134 Ndim = GD_ndim(g) = 2; /* The algorithm only makes sense in 2D */
135 mkClusters(g, NULL, g);
136 patchwork_init_node_edge(g);
137}
138
139/* patchwork_layout:
140 * The current version makes no use of edges, neither for a notion of connectivity
141 * nor during drawing.
142 */
143void patchwork_layout(Agraph_t *g)
144{
145 patchwork_init_graph(g);
146
147 if ((agnnodes(g) == 0) && (GD_n_cluster(g) == 0)) return;
148
149 patchworkLayout (g);
150
151 dotneato_postprocess(g);
152}
153
154static void patchwork_cleanup_graph(graph_t * g)
155{
156 free(GD_neato_nlist(g));
157 if (g != agroot(g))
158 agclean(g, AGRAPH , "Agraphinfo_t");
159}
160
161void patchwork_cleanup(graph_t * g)
162{
163 node_t *n;
164 edge_t *e;
165
166 n = agfstnode(g);
167 if (!n) return;
168 free (ND_alg(n));
169 for (; n; n = agnxtnode(g, n)) {
170 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
171 gv_cleanup_edge(e);
172 }
173 gv_cleanup_node(n);
174 }
175 patchwork_cleanup_graph(g);
176}
177