1
/* $Id: gvpack.c,v 1.14 2007/08/08 23:00:58 erg Exp $ $Revision: 1.14 $ */
1
/* $Id: gvpack.c,v 1.35 2010/01/08 21:28:39 erg Exp $ $Revision: 1.35 $ */
2
2
/* vim:set shiftwidth=4 ts=8: */
4
4
/**********************************************************
49
48
* into a single output graph, ready to be sent to neato -s -n2.
50
49
* -m <i> specifies the margin, in points, about each graph.
53
"gvpack", /* Program */
54
VERSION, /* Version */
55
BUILDDATE /* Build Date */
70
static int margin = 8; /* Default margin in packing */
71
static boolean doSplines = TRUE; /* Use edges in packing */
72
static pack_mode packMode = l_clust; /* Default packing mode - use clusters */
73
64
static int verbose = 0;
74
65
static char **myFiles = 0;
75
66
static int nGraphs = 0; /* Guess as to no. of graphs */
76
67
static FILE *outfp; /* output; stdout by default */
77
static int kind = -1; /* type of graph; -1 = undefined */
69
static int kind; /* type of graph */
71
static Agdesc_t kind; /* type of graph */
78
73
static int G_cnt; /* No. of -G arguments */
79
74
static int G_sz; /* Storage size for -G arguments */
80
75
static attr_t *G_args; /* Storage for -G arguments */
76
static int doPack; /* Do packing if true */
82
78
#define NEWNODE(n) ((node_t*)ND_alg(n))
83
#define DOPACK (packMode != l_undef)
85
80
static char *useString =
86
"Usage: gvpack [-gnuv?] [-m<margin>] [-o<outf>] <files>\n\
81
"Usage: gvpack [-gnuv?] [-m<margin>] {-array[_rc][n]] [-o<outf>] <files>\n\
87
82
-n - use node granularity\n\
88
83
-g - use graph granularity\n\
84
-array* - pack as array of graphs\n\
89
85
-G<n>=<v> - attach name/value attribute to output graph\n\
90
86
-m<n> - set margin to <n> points\n\
91
87
-o<outfile> - write output to <outfile>\n\
148
144
* If arg is an integer, value is stored in v
149
* and functions returns 0; otherwise, returns 1.
145
* and function returns 0; otherwise, returns 1.
151
static int setInt(int *v, char *arg)
147
static int setUInt(unsigned int *v, char *arg)
156
i = (int) strtol(arg, &p, 10);
152
i = (unsigned int) strtol(arg, &p, 10);
158
154
fprintf(stderr, "Error: bad value in flag -%s - ignored\n",
163
static Agsym_t *agraphattr(Agraph_t *g, char *name, char *value)
165
return agattr(g, AGRAPH, name, value);
168
static Agsym_t *agnodeattr(Agraph_t *g, char *name, char *value)
170
return agattr(g, AGNODE, name, value);
173
static Agsym_t *agedgeattr(Agraph_t *g, char *name, char *value)
175
return agattr(g, AGEDGE, name, value);
168
static void init(int argc, char *argv[])
182
static void init(int argc, char *argv[], pack_info* pinfo)
173
while ((c = getopt(argc, argv, ":ngvum:o:G:?")) != -1) {
191
agnodeattr(NULL, "label", NODENAME_ESC);
192
pinfo->mode = l_clust;
193
pinfo->margin = CL_OFFSET;
194
pinfo->doSplines = TRUE; /* Use edges in packing */
198
while ((c = getopt(argc, argv, ":na:gvum:o:G:")) != -1) {
201
len = strlen(optarg) + 2;
203
bp = N_GNEW(len, char);
206
sprintf (bp, "a%s\n", optarg);
207
parsePackModeInfo (bp, pinfo->mode, pinfo);
212
pinfo->mode = l_node;
215
pinfo->mode = l_graph;
182
setInt(&margin, optarg);
218
setUInt(&pinfo->margin, optarg);
185
221
outfp = openFile(optarg, "w");
224
pinfo->mode = l_undef;
229
265
int nG = agnnodes(g);
230
attrsym_t *N_pos = agfindattr(g->proto->n, "pos");
231
attrsym_t *N_pin = agfindattr(g->proto->n, "pin");
266
attrsym_t *N_pos = agfindnodeattr(g, "pos");
267
attrsym_t *N_pin = agfindnodeattr(g, "pin");
233
269
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
234
270
neato_init_node(n);
245
281
* libcommon. If fill is true, we use init_nop (neato -n) to
246
282
* read in attributes relevant to the layout.
248
static void init_graph(Agraph_t * g, boolean fill)
284
static void init_graph(Agraph_t * g, boolean fill, GVC_t* gvc)
289
aginit (g, AGRAPH, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);
290
aginit (g, AGNODE, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE);
291
aginit (g, AGEDGE, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE);
252
294
graph_init(g, FALSE);
253
d = late_int(g, agfindattr(g, "dim"), 2, 2);
295
d = late_int(g, agfindgraphattr(g, "dim"), 2, 2);
255
fprintf(stderr, "Error: graph %s has dim = %d (!= 2)\n", g->name,
297
fprintf(stderr, "Error: graph %s has dim = %d (!= 2)\n", agnameof(g),
267
309
* Copy all attributes from old object to new. Assume
268
310
* attributes have been initialized.
313
static void cloneDfltAttrs(Agraph_t *old, Agraph_t *new, int kind)
317
for (a = agnxtattr(old, kind, 0); a; a = agnxtattr(old, kind, a)) {
318
agattr (new, kind, a->name, a->defval);
321
static void cloneAttrs(void *old, void *new)
323
int kind = AGTYPE(old);
325
Agraph_t *g = agroot(old);
327
for (a = agnxtattr(g, kind, 0); a; a = agnxtattr(g, kind, a)) {
328
agset(new, a->name, agxget(old, a));
270
332
static void cloneAttrs(void *old, void *new)
299
362
static void cloneNode(Agnode_t * old, Agnode_t * new)
301
364
cloneAttrs(old, new);
302
ND_coord_i(new).x = POINTS(ND_pos(old)[0]);
303
ND_coord_i(new).y = POINTS(ND_pos(old)[1]);
365
ND_coord(new).x = POINTS(ND_pos(old)[0]);
366
ND_coord(new).y = POINTS(ND_pos(old)[1]);
304
367
ND_height(new) = ND_height(old);
305
ND_ht_i(new) = ND_ht_i(old);
368
ND_ht(new) = ND_ht(old);
306
369
ND_width(new) = ND_width(old);
307
ND_lw_i(new) = ND_lw_i(old);
308
ND_rw_i(new) = ND_rw_i(old);
370
ND_lw(new) = ND_lw(old);
371
ND_rw(new) = ND_rw(old);
309
372
ND_shape(new) = ND_shape(old);
310
373
ND_shape_info(new) = ND_shape_info(old);
344
407
* objp. If the attribute has already been defined and
345
408
* has a different default, set default to "".
411
static void fillDict(Dt_t * newdict, Agraph_t* g, int kind)
418
for (a = agnxtattr(g,kind,0); a; a = agnxtattr(g,kind,a)) {
421
rv = (attr_t *) dtmatch(newdict, name);
426
dtinsert(newdict, rv);
427
} else if (strcmp(value, rv->value))
347
432
static void fillDict(Dt_t * newdict, void *objp)
402
488
for (i = 0; i < cnt; i++) {
491
fillDict(g_attrs, g, AGRAPH);
492
fillDict(n_attrs, g, AGNODE);
493
fillDict(e_attrs, g, AGEDGE);
404
495
fillDict(g_attrs, g);
405
496
fillDict(n_attrs, g->proto->n);
406
497
fillDict(e_attrs, g->proto->e);
409
501
fillGraph(root, g_attrs, agraphattr);
420
512
static void cloneGraphAttr(Agraph_t * g, Agraph_t * ng)
422
514
cloneAttrs(g, ng);
516
cloneDfltAttrs(g, ng, AGNODE);
517
cloneDfltAttrs(g, ng, AGEDGE);
423
519
cloneAttrs(g->proto->n, ng->proto->n);
424
520
cloneAttrs(g->proto->e, ng->proto->e);
451
548
* dictionary names and the old name. If the old name has not
452
549
* been used, use it and add it to names. If it has been used,
453
550
* create a new name using the old name and a number.
551
* Note that returned string will immediately made into an agstring.
455
553
static char *xName(Dt_t * names, char *oldname)
555
static char* name = NULL;
556
static int namelen = 0;
461
561
p = (pair_t *) dtmatch(names, oldname);
564
len = strlen(oldname) + 100; /* 100 for "_gv" and decimal no. */
566
if (name) free (name);
567
name = N_NEW(len, char);
464
570
sprintf(name, "%s_gv%d", oldname, p->cnt);
475
581
#define MARK(e) (ED_alg(e) = e)
476
582
#define MARKED(e) (ED_alg(e))
477
#define ISCLUSTER(g) (!strncmp(g->name,"cluster",7))
583
#define ISCLUSTER(g) (!strncmp(agnameof(g),"cluster",7))
478
584
#define SETCLUST(g,h) (GD_alg(g) = h)
479
585
#define GETCLUST(g) ((Agraph_t*)GD_alg(g))
500
608
cloneGraphAttr(g, ng);
611
agxset(ng, G_bb, ""); /* Unset all subgraph bb */
502
613
agxset(ng, G_bb->index, ""); /* Unset all subgraph bb */
504
616
/* clone subgraphs */
618
for (subg = agfstsubg (g); subg; subg = agfstsubg (subg)) {
619
nsubg = agsubg(ng, xName(gnames, agnameof(subg)), 1);
505
621
mg = g->meta_node->graph;
506
622
for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
508
624
subg = agusergraph(mn);
509
nsubg = agsubg(ng, xName(gnames, subg->name));
625
nsubg = agsubg(ng, xName(gnames, agnameof(subg)));
510
627
cloneSubg(subg, nsubg, G_bb, gnames);
511
628
/* if subgraphs are clusters, point to the new
512
629
* one so we can find it later.
518
635
/* add remaining nodes */
519
636
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
521
if (!agfindnode(ng, nn->name)) {
639
if (!agfindnode(ng, agnameof(nn)))
522
640
aginsert(ng, nn);
642
agsubnode(ng, nn, 1);
526
646
/* add remaining edges. libgraph doesn't provide a way to find
530
650
for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
533
nt = NEWNODE(e->tail);
534
nh = NEWNODE(e->head);
653
nt = NEWNODE(agtail(e));
654
nh = NEWNODE(aghead(e));
535
656
ne = agedge(ng, nt, nh);
658
ne = agedge(ng, nt, nh, NULL, 1);
659
agbindrec (ne, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE);
536
661
cloneEdge(e, ne);
595
720
fprintf(stderr, "Creating clone graph\n");
596
722
root = agopen("root", kind);
724
root = agopen("root", kind, &AgDefaultDisc);
598
726
initAttrs(root, gs, cnt);
599
G_bb = agfindattr(root, "bb");
600
if (DOPACK) assert(G_bb);
727
G_bb = agfindgraphattr(root, "bb");
728
if (doPack) assert(G_bb);
602
730
/* add command-line attributes */
603
731
for (i = 0; i < G_cnt; i++) {
604
rv = agfindattr(root, G_args[i].name);
732
rv = agfindgraphattr(root, G_args[i].name);
606
735
agxset(root, rv->index, G_args[i].value);
608
737
agraphattr(root, G_args[i].name, G_args[i].value);
739
agxset(root, rv, G_args[i].value);
741
agattr(root, AGRAPH, G_args[i].name, G_args[i].value);
611
745
/* do common initialization. This will handle root's label. */
612
init_graph(root, FALSE);
746
init_graph(root, FALSE, gvc);
613
747
State = GVSPLINES;
615
749
gnames = dtopen(&pairdisc, Dtoset);
617
751
for (i = 0; i < cnt; i++) {
620
fprintf(stderr, "Cloning graph %s\n", g->name);
754
fprintf(stderr, "Cloning graph %s\n", agnameof(g));
621
755
GD_n_cluster(root) += GD_n_cluster(g);
623
757
/* Clone nodes, checking for node name conflicts */
624
758
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
625
if (doWarn && agfindnode(root, n->name)) {
759
if (doWarn && agfindnode(root, agnameof(n))) {
627
761
"Warning: node %s in graph[%d] %s already defined\n",
628
n->name, i, g->name);
762
agnameof(n), i, agnameof(g));
629
763
fprintf(stderr, "Some nodes will be renamed.\n");
632
np = agnode(root, xName(nnames, n->name));
767
np = agnode(root, xName(nnames, agnameof(n)));
769
np = agnode(root, xName(nnames, agnameof(n)), 1);
770
agbindrec (np, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE);
634
773
cloneNode(n, np);
637
776
/* wrap the clone of g in a subgraph of root */
638
subg = agsubg(root, xName(gnames, g->name));
778
subg = agsubg(root, xName(gnames, agnameof(g)));
780
subg = agsubg(root, xName(gnames, agnameof(g)), 1);
639
782
cloneSubg(g, subg, G_bb, gnames);
807
static Agraph_t *gread(FILE * fp)
809
return agread(fp, (Agdisc_t *) 0);
812
static Agraph_t *gread(FILE * fp)
664
819
* Read input, parse the graphs, use init_nop (neato -n) to
665
820
* read in all attributes need for layout.
677
832
ingraph_state ig;
681
837
/* set various state values */
682
838
PSinputscale = POINTS_PER_INCH;
685
newIngraph(&ig, myFiles, agread);
841
newIngraph(&ig, myFiles, gread);
686
842
while ((g = nextGraph(&ig)) != 0) {
689
fprintf(stderr, "Reading graph %s\n", g->name);
844
fprintf(stderr, "Reading graph %s\n", agnameof(g));
692
847
gs = ALLOC(sz, gs, Agraph_t *);
696
854
else if ((kind & AGFLAG_DIRECTED) != AG_IS_DIRECTED(g)) {
698
856
"Error: all graphs must be directed or undirected\n");
700
858
} else if (!AG_IS_STRICT(g))
702
init_graph(g, DOPACK);
865
else if (kind.directed != g->desc.directed) {
867
"Error: all graphs must be directed or undirected\n");
869
} else if (!agisstrict(g))
872
init_graph(g, doPack, gvc);
713
883
* Compute the bounding box containing the graphs.
714
884
* We can just use the bounding boxes of the graphs.
716
box compBB(Agraph_t ** gs, int cnt)
886
boxf compBB(Agraph_t ** gs, int cnt)
721
891
bb = GD_bb(gs[0]);
740
910
for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
741
911
fprintf(stderr, "%s\n", v->name);
742
912
for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
743
fprintf(stderr, " %s -- %s\n", e->tail->name, e->head->name);
913
fprintf(stderr, " %s -- %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
755
925
mg = g->meta_node->graph;
756
926
for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
758
928
subg = agusergraph(mn);
760
930
fprintf(stderr, "====\n");
776
gvc = gvNEWcontext(Info, gvUsername());
943
init(argc, argv, &pinfo);
945
doPack = (pinfo.mode != l_undef);
947
gvc = gvNEWcontext(lt_preloaded_symbols, DEMAND_LOADING);
777
948
gs = readGraphs(&cnt, gvc);
781
952
/* pack graphs */
783
pinfo.margin = margin;
784
pinfo.doSplines = doSplines;
785
pinfo.mode = packMode;
787
954
if (packGraphs(cnt, gs, 0, &pinfo)) {
788
955
fprintf(stderr, "gvpack: packing of graphs failed.\n");