Graphviz 16.1.1~dev.20260906.1627
Loading...
Searching...
No Matches
neatoinit.c
Go to the documentation of this file.
1
8/*************************************************************************
9 * Copyright (c) 2011 AT&T Intellectual Property
10 * All rights reserved. This program and the accompanying materials
11 * are made available under the terms of the Eclipse Public License v2.0
12 * which accompanies this distribution, and is available at
13 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
14 *
15 * Contributors: Details at https://graphviz.org
16 *************************************************************************/
17
18
19#include "config.h"
20
21#include <time.h>
22#include <neatogen/neato.h>
23#include <pack/pack.h>
24#include <neatogen/stress.h>
25#ifdef DIGCOLA
26#include <neatogen/digcola.h>
27#endif
28#include <neatogen/kkutils.h>
29#include <common/pointset.h>
30#include <common/render.h>
31#include <common/utils.h>
32#include <neatogen/sgd.h>
33#include <cgraph/cgraph.h>
34#include <float.h>
35#include <stdatomic.h>
36#include <stdbool.h>
37#include <stddef.h>
38#include <stdint.h>
39#include <util/agxbuf.h>
40#include <util/alloc.h>
41#include <util/bitarray.h>
42#include <util/gv_ctype.h>
43#include <util/gv_math.h>
44#include <util/itos.h>
45#include <util/prisize_t.h>
46#include <util/startswith.h>
47#include <util/strcasecmp.h>
48#include <util/streq.h>
49
50#ifndef HAVE_SRAND48
51#define srand48 srand
52#endif
53
55static int Pack; /* If >= 0, layout components separately and pack together
56 * The value of Pack gives margins around graphs.
57 */
58static char *cc_pfx = "_neato_cc";
59
61{
62 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
64 ND_pos(n) = gv_calloc(GD_ndim(agraphof(n)), sizeof(double));
66}
67
68static void neato_init_edge(edge_t * e)
69{
70 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
72 ED_factor(e) = late_double(e, E_weight, 1.0, 1.0);
73}
74
75bool user_pos(attrsym_t *posptr, attrsym_t *pinptr, node_t *np, int nG) {
76 double *pvec;
77 char *p, c;
78 double z;
79
80 if (posptr == NULL)
81 return false;
82 pvec = ND_pos(np);
83 p = agxget(np, posptr);
84 if (p[0]) {
85 c = '\0';
86 if (Ndim >= 3 && sscanf(p, "%lf,%lf,%lf%c", pvec, pvec+1, pvec+2, &c) >= 3){
87 ND_pinned(np) = P_SET;
88 if (PSinputscale > 0.0) {
89 int i;
90 for (i = 0; i < Ndim; i++)
91 pvec[i] /= PSinputscale;
92 }
93 if (Ndim > 3)
94 jitter_d(np, nG, 3);
95 if (c == '!' || (pinptr && mapbool(agxget(np, pinptr))))
96 ND_pinned(np) = P_PIN;
97 return true;
98 }
99 else if (sscanf(p, "%lf,%lf%c", pvec, pvec + 1, &c) >= 2) {
100 ND_pinned(np) = P_SET;
101 if (PSinputscale > 0.0) {
102 int i;
103 for (i = 0; i < Ndim; i++)
104 pvec[i] /= PSinputscale;
105 }
106 if (Ndim > 2) {
107 if (N_z && (p = agxget(np, N_z)) && sscanf(p,"%lf",&z) == 1) {
108 if (PSinputscale > 0.0) {
109 pvec[2] = z / PSinputscale;
110 }
111 else
112 pvec[2] = z;
113 jitter_d(np, nG, 3);
114 }
115 else
116 jitter3d(np, nG);
117 }
118 if (c == '!' || (pinptr && mapbool(agxget(np, pinptr))))
119 ND_pinned(np) = P_PIN;
120 return true;
121 } else
122 agerrorf("node %s, position %s, expected two doubles\n",
123 agnameof(np), p);
124 }
125 return false;
126}
127
129{
130 node_t *n;
131 edge_t *e;
132 int nG = agnnodes(g);
133 attrsym_t *N_pin;
134
135 N_pos = agfindnodeattr(g, "pos");
136 N_pin = agfindnodeattr(g, "pin");
137
138 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
140 user_pos(N_pos, N_pin, n, nG); /* set user position if given */
141 }
142 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
143 for (e = agfstout(g, n); e; e = agnxtout(g, e))
145 }
146}
147
149{
150 if (Nop || Pack < 0) {
152 }
153 free(GD_clust(g));
154}
155
157{
158 node_t *n;
159 edge_t *e;
160
161 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
162 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
164 }
166 }
168}
169
170static size_t numFields(const char *pos) {
171 size_t cnt = 0;
172 char c;
173
174 do {
175 while (gv_isspace(*pos))
176 pos++; /* skip white space */
177 if ((c = *pos)) { /* skip token */
178 cnt++;
179 while ((c = *pos) && !gv_isspace(c) && c != ';')
180 pos++;
181 }
182 } while (gv_isspace(c));
183 return cnt;
184}
185
186static void set_label(void* obj, textlabel_t * l, char *name)
187{
188 double x, y;
189 char *lp;
190 lp = agget(obj, name);
191 if (lp && sscanf(lp, "%lf,%lf", &x, &y) == 2) {
192 l->pos = (pointf){x, y};
193 l->set = true;
194 }
195}
196
197#ifdef IPSEPCOLA
198static cluster_data cluster_map(graph_t *mastergraph, graph_t *g) {
199 graph_t *subg;
200 node_t *n;
201 /* array of arrays of node indices in each cluster */
202 bitarray_t assigned = bitarray_new(agnnodes(g));
203 cluster_data cdata = {0};
204
205 size_t nclusters = 0;
206 cdata.ntoplevel = agnnodes_z(g);
207 for (subg = agfstsubg(mastergraph); subg; subg = agnxtsubg(subg)) {
208 if (is_a_cluster(subg)) {
209 nclusters++;
210 }
211 }
212 cdata.nvars=0;
213 cdata.nclusters = nclusters;
214 size_t **cs = cdata.clusters = gv_calloc(nclusters, sizeof(size_t *));
215 size_t *cn = cdata.clustersizes = gv_calloc(nclusters, sizeof(size_t));
216 for (subg = agfstsubg(mastergraph); subg; subg = agnxtsubg(subg)) {
217 /* clusters are processed by separate calls to ordered_edges */
218 if (is_a_cluster(subg)) {
219 *cn = agnnodes_z(subg);
220 cdata.nvars += *cn;
221 size_t *c = *cs++ = gv_calloc(*cn++, sizeof(size_t));
222 for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
223 node_t *gn;
224 size_t ind = 0;
225 for (gn = agfstnode(g); gn; gn = agnxtnode(g, gn)) {
226 if(AGSEQ(gn)==AGSEQ(n)) break;
227 ind++;
228 }
229 *c++=ind;
230 bitarray_set(&assigned, ind, true);
231 cdata.ntoplevel--;
232 }
233 }
234 }
235 cdata.bb = gv_calloc(cdata.nclusters, sizeof(boxf));
236 cdata.toplevel = gv_calloc(cdata.ntoplevel, sizeof(size_t));
237 size_t j;
238 for (size_t i = j = 0; i < agnnodes_z(g); i++) {
239 if(!bitarray_get(assigned, i)) {
240 cdata.toplevel[j++] = i;
241 }
242 }
243 assert(cdata.ntoplevel == agnnodes_z(g) - cdata.nvars);
244 bitarray_reset(&assigned);
245 return cdata;
246}
247
248static void freeClusterData(cluster_data c) {
249 if (c.nclusters > 0) {
250 free(c.clusters[0]);
251 free(c.clusters);
252 free(c.clustersizes);
253 free(c.toplevel);
254 free(c.bb);
255 }
256}
257#endif
258
259/* Attempt to use already existing pos info for spline
260 * Return 1 if successful, 0 otherwise.
261 * Assume E_pos != NULL and ED_spl(e) == NULL.
262 */
263static int user_spline(attrsym_t * E_pos, edge_t * e)
264{
265 int nc;
266 pointf *pp;
267 double x, y;
268 bool sflag = false, eflag = false;
269 pointf sp = { 0, 0 }, ep = { 0, 0};
270 bezier *newspl;
271 static atomic_flag warned;
272
273 const char *pos = agxget(e, E_pos);
274 if (*pos == '\0')
275 return 0;
276
277 uint32_t stype, etype;
278 arrow_flags(e, &stype, &etype);
279 for (bool more = true; more; ) {
280 /* check for s head */
281 if (sscanf(pos, "s,%lf,%lf%n", &x, &y, &nc) == 2) {
282 sflag = true;
283 pos += nc;
284 sp = (pointf){.x = x, .y = y};
285 }
286
287 /* check for e head */
288 if (sscanf(pos, " e,%lf,%lf%n", &x, &y, &nc) == 2) {
289 eflag = true;
290 pos += nc;
291 ep = (pointf){.x = x, .y = y};
292 }
293
294 const size_t npts = numFields(pos); // count potential points
295 if (npts < 4 || npts % 3 != 1) {
297 if (!atomic_flag_test_and_set(&warned)) {
298 agwarningf("pos attribute for edge (%s,%s) doesn't have 3n+1 points\n", agnameof(agtail(e)), agnameof(aghead(e)));
299 }
300 return 0;
301 }
302 pointf *ps = gv_calloc(npts, sizeof(pointf));
303 pp = ps;
304 for (size_t n = npts; n > 0; --n) {
305 if (sscanf(pos, "%lf,%lf%n", &x, &y, &nc) < 2) {
306 if (!atomic_flag_test_and_set(&warned)) {
307 agwarningf("syntax error in pos attribute for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
308 }
309 free(ps);
311 return 0;
312 }
313 pos += nc;
314 *pp = (pointf){.x = x, .y = y};
315 pp++;
316 }
317 while (gv_isspace(*pos)) pos++;
318 if (*pos == '\0')
319 more = false;
320 else
321 pos++;
322
323 /* parsed successfully; create spline */
324 newspl = new_spline(e, npts);
325 if (sflag) {
326 newspl->sflag = stype;
327 newspl->sp = sp;
328 }
329 if (eflag) {
330 newspl->eflag = etype;
331 newspl->ep = ep;
332 }
333 for (size_t i = 0; i < npts; i++) {
334 newspl->list[i] = ps[i];
335 }
336 free(ps);
337 }
338
339 if (ED_label(e))
340 set_label(e, ED_label(e), "lp");
341 if (ED_xlabel(e))
342 set_label(e, ED_xlabel(e), "xlp");
343 if (ED_head_label(e))
344 set_label(e, ED_head_label(e), "head_lp");
345 if (ED_tail_label(e))
346 set_label(e, ED_tail_label(e), "tail_lp");
347
348 return 1;
349}
350
351/* Nop can be:
352 * 0 - do full layout
353 * 1 - assume initial node positions, do (optional) adjust and all splines
354 * 2 - assume final node and edges positions, do nothing except compute
355 * missing splines
356 */
357
358 /* Indicates the amount of edges with position information */
360
361/* Check edges for position info.
362 * If position info exists, check for edge label positions.
363 * Return number of edges with position info.
364 */
366{
367 node_t *n;
368 edge_t *e;
369 int nedges = 0;
370
371 if (agnedges(g) == 0)
372 return AllEdges;
373
374 attrsym_t *const E_pos = agfindedgeattr(g, "pos");
375 if (!E_pos || Nop < 2)
376 return NoEdges;
377
378 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
379 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
380 if (user_spline(E_pos, e)) {
381 nedges++;
382 }
383 }
384 }
385 if (nedges) {
386 if (nedges == agnedges(g))
387 return AllEdges;
388 return SomeEdges;
389 }
390 return NoEdges;
391}
392
393static void freeEdgeInfo (Agraph_t * g)
394{
395 node_t *n;
396 edge_t *e;
397
398 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
399 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
405 }
406 }
407}
408
409/* chkBB:
410 * Scans for a correct bb attribute. If available, sets it
411 * in the graph and returns 1.
412 */
413static int chkBB(Agraph_t * g, attrsym_t * G_bb, boxf* bbp)
414{
415 char *s;
416 boxf bb;
417
418 s = agxget(g, G_bb);
419 if (sscanf(s, "%lf,%lf,%lf,%lf", &bb.LL.x, &bb.LL.y, &bb.UR.x,
420 &bb.UR.y) == 4) {
421 if (bb.LL.y > bb.UR.y) {
422 /* If the LL.y coordinate is bigger than the UR.y coordinate,
423 * we assume the input was produced using -y, so we normalize
424 * the bb.
425 */
426 SWAP(&bb.LL.y, &bb.UR.y);
427 }
428 *bbp = bb;
429 return 1;
430 }
431 return 0;
432}
433
434static void add_cluster(Agraph_t * g, Agraph_t * subg)
435{
436 int cno;
437 cno = ++(GD_n_cluster(g));
438 GD_clust(g) = gv_recalloc(GD_clust(g), GD_n_cluster(g), cno + 1,
439 sizeof(graph_t*));
440 GD_clust(g)[cno] = subg;
441 do_graph_label(subg);
442}
443
444
445static void nop_init_graphs(Agraph_t *, attrsym_t *, attrsym_t *);
446
447/* Process subgraph subg of parent graph g
448 * If subg is a cluster, add its bounding box, if any; attach to
449 * cluster array of parent, and recursively initialize subg.
450 * If not a cluster, recursively call this function on the subgraphs
451 * of subg, using parentg as the parent graph.
452 */
453static void
454dfs(Agraph_t * subg, Agraph_t * parentg, attrsym_t * G_lp, attrsym_t * G_bb)
455{
456 boxf bb;
457
458 if (is_a_cluster(subg) && chkBB(subg, G_bb, &bb)) {
459 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
460 GD_bb(subg) = bb;
461 add_cluster(parentg, subg);
462 nop_init_graphs(subg, G_lp, G_bb);
463 } else {
464 graph_t *sg;
465 for (sg = agfstsubg(subg); sg; sg = agnxtsubg(sg)) {
466 dfs(sg, parentg, G_lp, G_bb);
467 }
468 }
469}
470
471/* Read in clusters and graph label info.
472 * A subgraph is a cluster if its name starts with "cluster" and
473 * it has a valid bb.
474 */
475static void
477{
478 graph_t *subg;
479 char *s;
480 double x, y;
481
482 if (GD_label(g) && G_lp) {
483 s = agxget(g, G_lp);
484 if (sscanf(s, "%lf,%lf", &x, &y) == 2) {
485 GD_label(g)->pos = (pointf){x, y};
486 GD_label(g)->set = true;
487 }
488 }
489
490 if (!G_bb)
491 return;
492 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
493 dfs(subg, g, G_lp, G_bb);
494 }
495}
496
497/* This assumes all nodes have been positioned.
498 * It also assumes none of the relevant fields in A*info_t have been set.
499 * The input may provide additional position information for
500 * clusters, edges and labels. If certain position information
501 * is missing, init_nop will use a standard neato technique to
502 * supply it.
503 *
504 * If adjust is false, init_nop does nothing but initialize all
505 * of the basic graph information. No tweaking of positions or
506 * filling in edge splines is done.
507 *
508 * Returns 0 on normal success, 1 if layout has a background, and -1
509 * on failure.
510 */
512{
513 int i;
514 node_t *np;
515 pos_edge posEdges; /* How many edges have spline info */
516 attrsym_t *G_lp = agfindgraphattr(g, "lp");
517 attrsym_t *G_bb = agfindgraphattr(g, "bb");
518 int didAdjust = 0; /* Have nodes been moved? */
519 int haveBackground;
520 bool translate = !mapbool(agget(g, "notranslate"));
521
522 /* If G_bb not defined, define it */
523 if (!G_bb)
524 G_bb = agattr_text(g, AGRAPH, "bb", "");
525
526 scan_graph(g); /* mainly to set up GD_neato_nlist */
527 for (i = 0; (np = GD_neato_nlist(g)[i]); i++) {
528 if (!hasPos(np) && !startswith(agnameof(np), "cluster")) {
529 agerrorf("node %s in graph %s has no position\n",
530 agnameof(np), agnameof(g));
531 return -1;
532 }
533 if (ND_xlabel(np))
534 set_label(np, ND_xlabel(np), "xlp");
535 }
536 nop_init_graphs(g, G_lp, G_bb);
537 posEdges = nop_init_edges(g);
538
539 if (GD_drawing(g)->xdots) {
540 haveBackground = 1;
541 GD_drawing(g)->ratio_kind = R_NONE; /* Turn off any aspect change if background present */
542 }
543 else
544 haveBackground = 0;
545
546 if (adjust && Nop == 1 && !haveBackground)
547 didAdjust = adjustNodes(g);
548
549 if (didAdjust) {
550 if (GD_label(g)) GD_label(g)->set = false;
551/* FIX:
552 * - if nodes are moved, clusters are no longer valid.
553 */
554 }
555
556 compute_bb(g);
557
558 /* Adjust bounding box for any background */
559 if (haveBackground)
560 GD_bb(g) = xdotBB (g);
561
562 /* At this point, all bounding boxes should be correctly defined.
563 */
564
565 if (!adjust) {
566 node_t *n;
568 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
569 ND_coord(n).x = POINTS_PER_INCH * ND_pos(n)[0];
570 ND_coord(n).y = POINTS_PER_INCH * ND_pos(n)[1];
571 }
572 }
573 else {
574 bool didShift;
575 if (translate && !haveBackground && (GD_bb(g).LL.x != 0||GD_bb(g).LL.y != 0))
576 neato_translate (g);
577 didShift = neato_set_aspect(g);
578 /* if we have some edge positions and we either shifted or adjusted, free edge positions */
579 if (posEdges != NoEdges && (didShift || didAdjust)) {
580 freeEdgeInfo (g);
581 posEdges = NoEdges;
582 }
583 if (posEdges != AllEdges || Nop == 3)
584 spline_edges0(g, false); /* add edges */
585 else
587 }
588
589 return haveBackground;
590}
591
592static void neato_init_graph (Agraph_t * g)
593{
594 int outdim;
595
597 outdim = late_int(g, agfindgraphattr(g, "dimen"), 2, 2);
598 GD_ndim(agroot(g)) = late_int(g, agfindgraphattr(g, "dim"), outdim, 2);
599 Ndim = GD_ndim(g->root) = MIN(GD_ndim(g->root), MAXDIM);
600 GD_odim(g->root) = MIN(outdim, Ndim);
602}
603
604static int neatoModel(graph_t * g)
605{
606 char *p = agget(g, "model");
607
608 if (!p || streq(p, ""))
609 return MODEL_SHORTPATH;
610 if (streq(p, "circuit"))
611 return MODEL_CIRCUIT;
612 if (streq(p, "subset"))
613 return MODEL_SUBSET;
614 if (streq(p, "shortpath"))
615 return MODEL_SHORTPATH;
616 if (streq(p, "mds")) {
617 if (agattr_text(g, AGEDGE, "len", 0))
618 return MODEL_MDS;
619 else {
621 "edges in graph %s have no len attribute. Hence, the mds model\n", agnameof(g));
622 agerr(AGPREV, "is inappropriate. Reverting to the shortest path model.\n");
623 return MODEL_SHORTPATH;
624 }
625 }
627 "Unknown value %s for attribute \"model\" in graph %s - ignored\n",
628 p, agnameof(g));
629 return MODEL_SHORTPATH;
630}
631
632static int neatoMode(graph_t * g)
633{
634 char *str;
635 int mode = MODE_MAJOR; /* default mode */
636
637 str = agget(g, "mode");
638 if (str && !streq(str, "")) {
639 if (streq(str, "KK"))
640 mode = MODE_KK;
641 else if (streq(str, "major"))
643 else if (streq(str, "sgd"))
644 mode = MODE_SGD;
645#ifdef DIGCOLA
646 else if (streq(str, "hier"))
647 mode = MODE_HIER;
648#ifdef IPSEPCOLA
649 else if (streq(str, "ipsep"))
651#endif
652#endif
653 else
655 "Illegal value %s for attribute \"mode\" in graph %s - ignored\n",
656 str, agnameof(g));
657 }
658
659 return mode;
660}
661
662/* checkEdge:
663 *
664 */
665static int checkEdge(PointMap * pm, edge_t * ep, int idx)
666{
667 int i = ND_id(agtail(ep));
668 int j = ND_id(aghead(ep));
669
670 if (i > j) {
671 SWAP(&i, &j);
672 }
673 return insertPM(pm, i, j, idx);
674}
675
676#ifdef DIGCOLA
677/* dfsCycle:
678 * dfs for breaking cycles in vtxdata
679 */
680static void
681dfsCycle (vtx_data* graph, int i,int mode, node_t* nodes[])
682{
683 node_t *np, *hp;
684 int j;
685 /* if mode is IPSEP make it an in-edge
686 * at both ends, so that an edge constraint won't be generated!
687 */
688 const int8_t x = mode == MODE_IPSEP ? -1 : 1;
689
690 np = nodes[i];
691 ND_mark(np) = true;
692 ND_onstack(np) = true;
693 for (size_t e = 1; e < graph[i].nedges; e++) {
694 if (graph[i].edists[e] == 1) continue; // in edge
695 j = graph[i].edges[e];
696 hp = nodes[j];
697 if (ND_onstack(hp)) { /* back edge: reverse it */
698 graph[i].edists[e] = x;
699 size_t f;
700 for (f = 1; f < graph[j].nedges && graph[j].edges[f] != i; f++) ;
701 assert (f < graph[j].nedges);
702 graph[j].edists[f] = -1;
703 }
704 else if (!ND_mark(hp)) dfsCycle(graph, j, mode, nodes);
705
706 }
707 ND_onstack(np) = false;
708}
709
711static void
712acyclic (vtx_data* graph, int nv, int mode, node_t* nodes[])
713{
714 int i;
715 node_t* np;
716
717 for (i = 0; i < nv; i++) {
718 np = nodes[i];
719 ND_mark(np) = false;
720 ND_onstack(np) = false;
721 }
722 for (i = 0; i < nv; i++) {
723 if (ND_mark(nodes[i])) continue;
724 dfsCycle (graph, i, mode, nodes);
725 }
726
727}
728#endif
729
730/* Create sparse graph representation via arrays.
731 * Each node is represented by a vtx_data.
732 * The index of each neighbor is stored in the edges array;
733 * the corresponding edge lengths and weights go on ewgts and eweights.
734 * We do not allocate the latter 2 if the graph does not use them.
735 * By convention, graph[i].edges[0] == i.
736 * The values graph[i].ewgts[0] and graph[i].eweights[0] are left undefined.
737 *
738 * In constructing graph from g, we neglect loops. We track multiedges (ignoring
739 * direction). Edge weights are additive; the final edge length is the max.
740 *
741 * If direction is used, we set the edists field, -1 for tail, +1 for head.
742 * graph[i].edists[0] is left undefined. If multiedges exist, the direction
743 * of the first one encountered is used. Finally, a pass is made to guarantee
744 * the graph is acyclic.
745 *
746 */
747static vtx_data *makeGraphData(graph_t * g, int nv, int *nedges, int mode, int model, node_t*** nodedata)
748{
749 int ne = agnedges(g); /* upper bound */
750 float *ewgts = NULL;
751 node_t *np;
752 edge_t *ep;
753 float *eweights = NULL;
754#ifdef DIGCOLA
755 int8_t *edists = NULL;
756#endif
757 PointMap *ps = newPM();
758 int i, idx;
759
760 /* lengths and weights unused in reweight model */
761 bool haveLen = false;
762 bool haveWt = false;
763 if (model != MODEL_SUBSET) {
764 haveLen = agattr_text(g, AGEDGE, "len", 0) != NULL;
765 haveWt = E_weight != 0;
766 }
767 bool haveDir = mode == MODE_HIER || mode == MODE_IPSEP;
768
769 vtx_data *graph = gv_calloc(nv, sizeof(vtx_data));
770 node_t** nodes = gv_calloc(nv, sizeof(node_t*));
771 const size_t edges_size = (size_t)(2 * ne + nv);
772 int *edges = gv_calloc(edges_size, sizeof(int)); // reserve space for self loops
773 if (haveLen || haveDir)
774 ewgts = gv_calloc(edges_size, sizeof(float));
775 if (haveWt)
776 eweights = gv_calloc(edges_size, sizeof(float));
777#ifdef DIGCOLA
778 if (haveDir)
779 edists = gv_calloc(edges_size, sizeof(int8_t));
780#endif
781
782 i = 0;
783 ne = 0;
784 for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
785 int j = 1; /* index of neighbors */
786 clearPM(ps);
787 assert(ND_id(np) == i);
788 nodes[i] = np;
789 graph[i].edges = edges++; /* reserve space for the self loop */
790 if (haveLen || haveDir)
791 graph[i].ewgts = ewgts++;
792 else
793 graph[i].ewgts = NULL;
794 if (haveWt)
795 graph[i].eweights = eweights++;
796 else
797 graph[i].eweights = NULL;
798#ifdef DIGCOLA
799 if (haveDir) {
800 graph[i].edists = edists++;
801 }
802 else
803 graph[i].edists = NULL;
804#endif
805 size_t i_nedges = 1; // one for the self
806
807 for (ep = agfstedge(g, np); ep; ep = agnxtedge(g, ep, np)) {
808 if (aghead(ep) == agtail(ep))
809 continue; /* ignore loops */
810 idx = checkEdge(ps, ep, j);
811 if (idx != j) { /* seen before */
812 if (haveWt)
813 graph[i].eweights[idx] += ED_factor(ep);
814 if (haveLen) {
815 graph[i].ewgts[idx] = fmax(graph[i].ewgts[idx], ED_dist(ep));
816 }
817 } else {
818 node_t *vp = agtail(ep) == np ? aghead(ep) : agtail(ep);
819 ne++;
820 j++;
821
822 *edges++ = ND_id(vp);
823 if (haveWt)
824 *eweights++ = ED_factor(ep);
825 if (haveLen)
826 *ewgts++ = ED_dist(ep);
827 else if (haveDir)
828 *ewgts++ = 1.0;
829#ifdef DIGCOLA
830 if (haveDir) {
831 char *s = agget(ep,"dir");
832 if(s && startswith(s, "none")) {
833 *edists++ = 0;
834 } else {
835 *edists++ = np == aghead(ep) ? 1 : -1;
836 }
837 }
838#endif
839 i_nedges++;
840 }
841 }
842
843 graph[i].nedges = i_nedges;
844 graph[i].edges[0] = i;
845 i++;
846 }
847#ifdef DIGCOLA
848 if (haveDir) {
849 /* Make graph acyclic */
850 acyclic (graph, nv, mode, nodes);
851 }
852#endif
853
854 ne /= 2; /* every edge is counted twice */
855
856 /* If necessary, release extra memory. */
857 if (ne != agnedges(g)) {
858 edges = gv_recalloc(graph[0].edges, edges_size, 2 * ne + nv, sizeof(int));
859 if (haveLen)
860 ewgts = gv_recalloc(graph[0].ewgts, edges_size, 2 * ne + nv, sizeof(float));
861 if (haveWt)
862 eweights = gv_recalloc(graph[0].eweights, edges_size, 2 * ne + nv, sizeof(float));
863
864 for (i = 0; i < nv; i++) {
865 const size_t sz = graph[i].nedges;
866 graph[i].edges = edges;
867 edges += sz;
868 if (haveLen) {
869 graph[i].ewgts = ewgts;
870 ewgts += sz;
871 }
872 if (haveWt) {
873 graph[i].eweights = eweights;
874 eweights += sz;
875 }
876 }
877 }
878
879 *nedges = ne;
880 if (nodedata)
881 *nodedata = nodes;
882 else
883 free (nodes);
884 freePM(ps);
885 return graph;
886}
887
888static void initRegular(graph_t * G, int nG)
889{
890 double a, da;
891 node_t *np;
892
893 a = 0.0;
894 da = 2 * M_PI / nG;
895 for (np = agfstnode(G); np; np = agnxtnode(G, np)) {
896 ND_pos(np)[0] = nG * Spring_coeff * cos(a);
897 ND_pos(np)[1] = nG * Spring_coeff * sin(a);
898 ND_pinned(np) = P_SET;
899 a = a + da;
900 if (Ndim > 2)
901 jitter3d(np, nG);
902 }
903}
904
905#define SLEN(s) (sizeof(s)-1)
906#define SMART "self"
907#define REGULAR "regular"
908#define RANDOM "random"
909
910/* Analyze "start" attribute. If unset, return dflt.
911 * If it begins with self, regular, or random, return set init to same,
912 * else set init to dflt.
913 * If init is random, look for value integer suffix to use a seed; if not
914 * found, use time to set seed and store seed in graph.
915 * Return seed in seedp.
916 * Return init.
917 */
918int
919setSeed (graph_t * G, int dflt, long* seedp)
920{
921 char *p = agget(G, "start");
922 int init = dflt;
923
924 if (!p || *p == '\0') return dflt;
925 if (gv_isalpha(*p)) {
926 if (startswith(p, SMART)) {
927 init = INIT_SELF;
928 p += SLEN(SMART);
929 } else if (startswith(p, REGULAR)) {
931 p += SLEN(REGULAR);
932 } else if (startswith(p, RANDOM)) {
934 p += SLEN(RANDOM);
935 }
936 else init = dflt;
937 }
938 else if (gv_isdigit(*p)) {
940 }
941
942 if (init == INIT_RANDOM) {
943 long seed;
944 /* Check for seed value */
945 if (!gv_isdigit(*p) || sscanf(p, "%ld", &seed) < 1) {
946 seed = (unsigned) time(NULL);
947 agset(G, "start", ITOS(seed));
948 }
949 *seedp = seed;
950 }
951 return init;
952}
953
954/* Allow various weights for the scale factor in used to calculate stress.
955 * At present, only 1 or 2 are allowed, with 2 the default.
956 */
957#define exp_name "stresswt"
958
959static int checkExp (graph_t * G)
960{
961 int exp = late_int(G, agfindgraphattr(G, exp_name), 2, 0);
962 if (exp == 0 || exp > 2) {
963 agwarningf("%s attribute value must be 1 or 2 - ignoring\n", exp_name);
964 exp = 2;
965 }
966 return exp;
967}
968
969/* Analyzes start attribute, setting seed.
970 * If set,
971 * If start is regular, places nodes and returns INIT_REGULAR.
972 * If start is self, returns INIT_SELF.
973 * If start is random, returns INIT_RANDOM
974 * Set RNG seed
975 * else return default
976 *
977 */
978int checkStart(graph_t * G, int nG, int dflt)
979{
980 long seed;
981 int init;
982
983 seed = 1;
984 init = setSeed (G, dflt, &seed);
985 if (N_pos && init != INIT_RANDOM) {
986 agwarningf("node positions are ignored unless start=random\n");
987 }
988 if (init == INIT_REGULAR) initRegular(G, nG);
989 srand48(seed);
990 return init;
991}
992
993#ifdef DEBUG_COLA
994void dumpData(graph_t * g, vtx_data * gp, int nv, int ne)
995{
996 node_t *v;
997 int i;
998
999 fprintf(stderr, "#nodes %d #edges %d\n", nv, ne);
1000 for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
1001 fprintf(stderr, "\"%s\" %d\n", agnameof(v), ND_id(v));
1002 }
1003 for (i = 0; i < nv; i++) {
1004 const size_t n = gp[i].nedges;
1005 fprintf(stderr, "[%d] %" PRISIZE_T "\n", i, n);
1006 for (size_t j = 0; j < n; j++) {
1007 fprintf(stderr, " %3d", gp[i].edges[j]);
1008 }
1009 fputs("\n", stderr);
1010 if (gp[i].ewgts) {
1011 fputs(" ewgts", stderr);
1012 for (size_t j = 0; j < n; j++) {
1013 fprintf(stderr, " %3f", gp[i].ewgts[j]);
1014 }
1015 fputs("\n", stderr);
1016 }
1017 if (gp[i].eweights) {
1018 fputs(" eweights", stderr);
1019 for (size_t j = 0; j < n; j++) {
1020 fprintf(stderr, " %3f", gp[i].eweights[j]);
1021 }
1022 fputs("\n", stderr);
1023 }
1024 if (gp[i].edists) {
1025 fputs(" edists", stderr);
1026 for (size_t j = 0; j < n; j++) {
1027 fprintf(stderr, " %" PRId8, gp[i].edists[j]);
1028 }
1029 fputs("\n", stderr);
1030 }
1031 fputs("\n", stderr);
1032
1033 }
1034}
1035void dumpClusterData (cluster_data* dp)
1036{
1037 fprintf(stderr, "nvars %" PRISIZE_T " nclusters %" PRISIZE_T " ntoplevel %"
1038 PRISIZE_T "\n", dp->nvars, dp->nclusters, dp->ntoplevel);
1039 fprintf (stderr, "Clusters:\n");
1040 for (size_t i = 0; i < dp->nclusters; i++) {
1041 const size_t sz = dp->clustersizes[i];
1042 fprintf (stderr, " [%" PRISIZE_T "] %" PRISIZE_T " vars\n", i, sz);
1043 for (size_t j = 0; j < sz; j++)
1044 fprintf (stderr, " %" PRISIZE_T, dp->clusters[i][j]);
1045 fprintf (stderr, "\n");
1046 }
1047
1048
1049 fprintf (stderr, "Toplevel:\n");
1050 for (size_t i = 0; i < dp->ntoplevel; i++)
1051 fprintf (stderr, " %" PRISIZE_T "\n", dp->toplevel[i]);
1052
1053 fprintf (stderr, "Boxes:\n");
1054 for (size_t i = 0; i < dp->nclusters; i++) {
1055 boxf bb = dp->bb[i];
1056 fprintf (stderr, " (%f,%f) (%f,%f)\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
1057 }
1058}
1059void dumpOpts (ipsep_options* opp, int nv)
1060{
1061 int i;
1062
1063 fprintf (stderr, "diredges %d edge_gap %f noverlap %d gap (%f,%f)\n", opp->diredges, opp->edge_gap, opp->noverlap, opp->gap.x, opp->gap.y);
1064 for (i = 0; i < nv; i++)
1065 fprintf (stderr, " (%f,%f)\n", opp->nsize[i].x, opp->nsize[i].y);
1066 if (opp->clusters)
1067 dumpClusterData (opp->clusters);
1068}
1069#endif
1070
1071/* Solve stress using majorization.
1072 * Old neato attributes to incorporate:
1073 * weight
1074 * mode will be MODE_MAJOR, MODE_HIER or MODE_IPSEP
1075 */
1076static void
1077majorization(graph_t *mg, graph_t * g, int nv, int mode, int model, int dim, adjust_data* am)
1078{
1079#if !defined(DIGCOLA) || !defined(IPSEPCOLA)
1080 (void)mg;
1081 (void)am;
1082#endif
1083
1084 int ne;
1085 int rv = 0;
1086 node_t *v;
1087 vtx_data *gp;
1088 node_t** nodes;
1089 int init = checkStart(g, nv, mode == MODE_HIER ? INIT_SELF : INIT_RANDOM);
1090 int opts = checkExp (g);
1091
1092 if (init == INIT_SELF)
1094
1095 double **coords = gv_calloc(dim, sizeof(double *));
1096 coords[0] = gv_calloc(nv * dim, sizeof(double));
1097 for (int i = 1; i < Ndim; i++) {
1098 coords[i] = coords[0] + i * nv;
1099 }
1100 if (Verbose) {
1101 fprintf(stderr, "model %d smart_init %d stresswt %d iterations %d tol %f\n",
1103 fprintf(stderr, "convert graph: ");
1104 start_timer();
1105 fprintf(stderr, "majorization\n");
1106 }
1107 gp = makeGraphData(g, nv, &ne, mode, model, &nodes);
1108
1109 if (Verbose) {
1110 fprintf(stderr, "%d nodes %.2f sec\n", nv, elapsed_sec());
1111 }
1112
1113#ifdef DIGCOLA
1114 if (mode != MODE_MAJOR) {
1115 double lgap = late_double(g, agfindgraphattr(g, "levelsgap"), 0.0, -DBL_MAX);
1116 if (mode == MODE_HIER) {
1117 rv = stress_majorization_with_hierarchy(gp, nv, coords, nodes, Ndim,
1118 opts, model, MaxIter, lgap);
1119 }
1120#ifdef IPSEPCOLA
1121 else {
1122 char* str;
1123 ipsep_options opt;
1124 cluster_data cs = cluster_map(mg,g);
1125 pointf *nsize = gv_calloc(nv, sizeof(pointf));
1126 opt.edge_gap = lgap;
1127 opt.nsize = nsize;
1128 opt.clusters = cs;
1129 str = agget(g, "diredgeconstraints");
1130 if (mapbool(str)) {
1131 opt.diredges = 1;
1132 if(Verbose)
1133 fprintf(stderr,"Generating Edge Constraints...\n");
1134 } else if (str && !strncasecmp(str,"hier",4)) {
1135 opt.diredges = 2;
1136 if(Verbose)
1137 fprintf(stderr,"Generating DiG-CoLa Edge Constraints...\n");
1138 }
1139 else opt.diredges = 0;
1140 if (am->mode == AM_IPSEP) {
1141 opt.noverlap = 1;
1142 if(Verbose)
1143 fprintf(stderr,"Generating Non-overlap Constraints...\n");
1144 } else if (am->mode == AM_VPSC) {
1145 opt.noverlap = 2;
1146 if(Verbose)
1147 fprintf(stderr,"Removing overlaps as postprocess...\n");
1148 }
1149 else opt.noverlap = 0;
1150 const expand_t margin = sepFactor (g);
1151 /* Multiply by 2 since opt.gap is the gap size, not the margin */
1152 if (margin.doAdd) {
1153 opt.gap.x = 2.0*PS2INCH(margin.x);
1154 opt.gap.y = 2.0*PS2INCH(margin.y);
1155 }
1156 else opt.gap.x = opt.gap.y = 2.0*PS2INCH(DFLT_MARGIN);
1157 if(Verbose)
1158 fprintf(stderr,"gap=%f,%f\n",opt.gap.x,opt.gap.y);
1159 {
1160 size_t i = 0;
1161 for (v = agfstnode(g); v; v = agnxtnode(g, v),i++) {
1162 nsize[i].x = ND_width(v);
1163 nsize[i].y = ND_height(v);
1164 }
1165 }
1166
1167#ifdef DEBUG_COLA
1168 fprintf (stderr, "nv %d ne %d Ndim %d model %d MaxIter %d\n", nv, ne, Ndim, model, MaxIter);
1169 fprintf (stderr, "Nodes:\n");
1170 for (int i = 0; i < nv; i++) {
1171 fprintf (stderr, " %s (%f,%f)\n", nodes[i]->name, coords[0][i], coords[1][i]);
1172 }
1173 fprintf (stderr, "\n");
1174 dumpData(g, gp, nv, ne);
1175 fprintf (stderr, "\n");
1176 dumpOpts (&opt, nv);
1177#endif
1178 rv = stress_majorization_cola(gp, nv, coords, nodes, Ndim, model, MaxIter, &opt);
1179 freeClusterData(cs);
1180 free (nsize);
1181 }
1182#endif
1183 }
1184 else
1185#endif
1186 rv = stress_majorization_kD_mkernel(gp, nv, coords, nodes, Ndim, opts, model, MaxIter);
1187
1188 if (rv < 0) {
1189 agerr(AGPREV, "layout aborted\n");
1190 }
1191 else for (v = agfstnode(g); v; v = agnxtnode(g, v)) { /* store positions back in nodes */
1192 int idx = ND_id(v);
1193 for (int i = 0; i < Ndim; i++) {
1194 ND_pos(v)[i] = coords[i][idx];
1195 }
1196 }
1197 freeGraphData(gp);
1198 free(coords[0]);
1199 free(coords);
1200 free(nodes);
1201}
1202
1203static void subset_model(Agraph_t * G, int nG)
1204{
1205 int i, j, ne;
1206 vtx_data *gp;
1207
1208 gp = makeGraphData(G, nG, &ne, MODE_KK, MODEL_SUBSET, NULL);
1210 for (i = 0; i < nG; i++) {
1211 for (j = 0; j < nG; j++) {
1212 GD_dist(G)[i][j] = Dij[i][j];
1213 }
1214 }
1215 free(Dij[0]);
1216 free(Dij);
1217 freeGraphData(gp);
1218}
1219
1220/* Assume the matrix already contains shortest path values.
1221 * Use the actual lengths provided the input for edges.
1222 */
1223static void mds_model(graph_t * g)
1224{
1225 long i, j;
1226 node_t *v;
1227 edge_t *e;
1228
1229 for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
1230 for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
1231 i = AGSEQ(agtail(e));
1232 j = AGSEQ(aghead(e));
1233 if (i == j)
1234 continue;
1235 GD_dist(g)[i][j] = GD_dist(g)[j][i] = ED_dist(e);
1236 }
1237 }
1238}
1239
1241static void kkNeato(Agraph_t * g, int nG, int model)
1242{
1243 if (model == MODEL_SUBSET) {
1244 subset_model(g, nG);
1245 } else if (model == MODEL_CIRCUIT) {
1246 if (!circuit_model(g, nG)) {
1247 agwarningf(
1248 "graph %s is disconnected. Hence, the circuit model\n",
1249 agnameof(g));
1250 agerr(AGPREV,
1251 "is undefined. Reverting to the shortest path model.\n");
1252 agerr(AGPREV,
1253 "Alternatively, consider running neato using -Gpack=true or decomposing\n");
1254 agerr(AGPREV, "the graph into connected components.\n");
1255 shortest_path(g, nG);
1256 }
1257 } else if (model == MODEL_MDS) {
1258 shortest_path(g, nG);
1259 mds_model(g);
1260 } else
1261 shortest_path(g, nG);
1262 initial_positions(g, nG);
1263 diffeq_model(g, nG);
1264 if (Verbose) {
1265 fprintf(stderr, "Solving model %d iterations %d tol %f\n",
1266 model, MaxIter, Epsilon);
1267 start_timer();
1268 }
1269 solve_model(g, nG);
1270}
1271
1273static void
1274neatoLayout(Agraph_t * mg, Agraph_t * g, int layoutMode, int layoutModel,
1275 adjust_data* am)
1276{
1277 int nG;
1278 char *str;
1279
1280 if ((str = agget(g, "maxiter")))
1281 MaxIter = atoi(str);
1282 else if (layoutMode == MODE_MAJOR)
1284 else if (layoutMode == MODE_SGD)
1285 MaxIter = 30;
1286 else
1287 MaxIter = 100 * agnnodes(g);
1288
1289 nG = scan_graph_mode(g, layoutMode);
1290 if (nG < 2 || MaxIter < 0)
1291 return;
1292 if (layoutMode == MODE_KK)
1293 kkNeato(g, nG, layoutModel);
1294 else if (layoutMode == MODE_SGD)
1295 sgd(g, layoutModel);
1296 else
1297 majorization(mg, g, nG, layoutMode, layoutModel, Ndim, am);
1298}
1299
1300/* If dimension == 3 and z attribute is declared,
1301 * attach z value to nodes if not defined.
1302 */
1303static void addZ (Agraph_t* g)
1304{
1305 node_t* n;
1306 agxbuf buf = {0};
1307
1308 if (Ndim >= 3 && N_z) {
1309 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1310 agxbprint(&buf, "%lf", POINTS_PER_INCH * ND_pos(n)[2]);
1311 agxset(n, N_z, agxbuse(&buf));
1312 }
1313 }
1314 agxbfree(&buf);
1315}
1316
1317#ifdef IPSEPCOLA
1318static void
1319addCluster (graph_t* g)
1320{
1321 graph_t *subg;
1322 for (subg = agfstsubg(agroot(g)); subg; subg = agnxtsubg(subg)) {
1323 if (is_a_cluster(subg)) {
1324 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
1325 add_cluster(g, subg);
1326 compute_bb(subg);
1327 }
1328 }
1329}
1330#endif
1331
1332/* Simple wrapper to compute graph's bb, then route edges after
1333 * a possible aspect ratio adjustment.
1334 */
1335static void doEdges(Agraph_t* g)
1336{
1337 compute_bb(g);
1338 spline_edges0(g, true);
1339}
1340
1342{
1343 int layoutMode;
1344 int model;
1346 pack_info pinfo;
1347 adjust_data am;
1348 double save_scale = PSinputscale;
1349
1350 if (Nop) {
1351 int ret;
1354 addZ (g);
1355 ret = init_nop(g, 1);
1356 if (ret < 0) {
1357 agerr(AGPREV, "as required by the -n flag\n");
1358 return;
1359 }
1360 else gv_postprocess(g, 0);
1361 } else {
1362 bool noTranslate = mapbool(agget(g, "notranslate"));
1365 layoutMode = neatoMode(g);
1366 graphAdjustMode (g, &am, 0);
1367 model = neatoModel(g);
1368 mode = getPackModeInfo (g, l_undef, &pinfo);
1369 Pack = getPack(g, -1, CL_OFFSET);
1370 /* pack if just packmode defined. */
1371 if (mode == l_undef) {
1372 /* If the user has not indicated packing but we are
1373 * using the new neato, turn packing on.
1374 */
1375 if (Pack < 0 && layoutMode)
1376 Pack = CL_OFFSET;
1377 pinfo.mode = l_node;
1378 } else if (Pack < 0)
1379 Pack = CL_OFFSET;
1380 if (Pack >= 0) {
1381 graph_t *gc;
1382 graph_t **cc;
1383 size_t n_cc;
1384 bool pin;
1385
1386 cc = pccomps(g, &n_cc, cc_pfx, &pin);
1387
1388 if (n_cc > 1) {
1389 bool *bp;
1390 for (size_t i = 0; i < n_cc; i++) {
1391 gc = cc[i];
1392 (void)graphviz_node_induce(gc, NULL);
1393 neatoLayout(g, gc, layoutMode, model, &am);
1394 removeOverlapWith(gc, &am);
1396 if (noTranslate) doEdges(gc);
1397 else spline_edges(gc);
1398 }
1399 if (pin) {
1400 bp = gv_calloc(n_cc, sizeof(bool));
1401 bp[0] = true;
1402 } else
1403 bp = NULL;
1404 pinfo.margin = (unsigned)Pack;
1405 pinfo.fixed = bp;
1406 pinfo.doSplines = true;
1407 packGraphs(n_cc, cc, g, &pinfo);
1408 free(bp);
1409 }
1410 else {
1411 neatoLayout(g, g, layoutMode, model, &am);
1412 removeOverlapWith(g, &am);
1413 if (noTranslate) doEdges(g);
1414 else spline_edges(g);
1415 }
1416 compute_bb(g);
1417 addZ (g);
1418
1419 /* cleanup and remove component subgraphs */
1420 for (size_t i = 0; i < n_cc; i++) {
1421 gc = cc[i];
1422 free_scan_graph(gc);
1423 agdelrec (gc, "Agraphinfo_t");
1424 agdelete(g, gc);
1425 }
1426 free (cc);
1427#ifdef IPSEPCOLA
1428 addCluster (g);
1429#endif
1430 } else {
1431 neatoLayout(g, g, layoutMode, model, &am);
1432 removeOverlapWith(g, &am);
1433 addZ (g);
1434 if (noTranslate) doEdges(g);
1435 else spline_edges(g);
1436 }
1437 gv_postprocess(g, !noTranslate);
1438 }
1439 PSinputscale = save_scale;
1440}
1441
expand_t sepFactor(graph_t *g)
Definition adjust.c:1046
int adjustNodes(graph_t *G)
Definition adjust.c:999
void graphAdjustMode(graph_t *G, adjust_data *dp, char *dflt)
Definition adjust.c:863
int removeOverlapWith(graph_t *G, adjust_data *am)
Definition adjust.c:899
#define DFLT_MARGIN
Definition adjust.h:24
@ AM_VPSC
Definition adjust.h:31
@ AM_IPSEP
Definition adjust.h:31
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define Epsilon
Definition arcball.h:137
#define MIN(a, b)
Definition arith.h:28
#define M_PI
Definition arith.h:41
void arrow_flags(Agedge_t *e, uint32_t *sflag, uint32_t *eflag)
Definition arrows.c:218
API for compacted arrays of booleans.
static bitarray_t bitarray_new(size_t size_bits)
create an array of the given element length
Definition bitarray.h:47
static bool bitarray_get(bitarray_t self, size_t index)
get the value of the given element
Definition bitarray.h:65
static void bitarray_set(bitarray_t *self, size_t index, bool value)
set or clear the value of the given element
Definition bitarray.h:80
static void bitarray_reset(bitarray_t *self)
free underlying resources and leave a bit array empty
Definition bitarray.h:114
abstract graph C library, Cgraph API
int circuit_model(graph_t *g, int nG)
Definition circuit.c:38
static bool doEdges
induce edges
Definition ccomps.c:68
bool mapbool(const char *p)
Definition utils.c:341
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1423
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:40
void common_init_node(node_t *n)
Definition utils.c:427
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:55
void common_init_edge(edge_t *e)
Definition utils.c:509
double get_inputscale(graph_t *g)
Definition utils.c:78
void compute_bb(graph_t *g)
Definition utils.c:633
void gv_nodesize(node_t *n, bool flip)
Definition utils.c:1536
bool is_a_cluster(Agraph_t *g)
Definition utils.c:695
#define P_PIN
Definition const.h:249
#define P_SET
Definition const.h:247
#define CL_OFFSET
Definition const.h:142
#define MAXDIM
Definition const.h:160
#define EDGETYPE_LINE
Definition const.h:235
#define Spring_coeff
Definition const.h:158
#define GVSPLINES
Definition const.h:164
mode
Definition cvtgxl.c:33
void freeGraphData(vtx_data *graph)
Definition delaunay.c:654
static void init(int argc, char *argv[], double *angle, double *accuracy, int *check_edges_with_same_endpoint, int *seed, const char **color_scheme, int *lightness)
static const char adjust[]
Definition emit.c:3078
boxf xdotBB(Agraph_t *g)
Definition emit.c:3144
static long seed
Definition exeval.c:1014
#define G
Definition gdefs.h:7
#define PS2INCH(a_points)
Definition geom.h:64
struct pointf_s pointf
#define POINTS_PER_INCH
Definition geom.h:58
Agsym_t * E_weight
Definition globals.h:83
int State
Definition globals.h:66
int MaxIter
Definition globals.h:64
int Nop
Definition globals.h:57
Agsym_t * N_z
Definition globals.h:81
double PSinputscale
Definition globals.h:58
unsigned short Ndim
Definition globals.h:65
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
int agnedges(Agraph_t *g)
Definition graph.c:169
int agnnodes(Agraph_t *g)
Definition graph.c:163
size_t agnnodes_z(const Agraph_t *g)
Definition graph.c:161
size_t graphviz_node_induce(Agraph_t *g, Agraph_t *edgeset)
Definition node_induce.c:12
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:333
int agset(void *obj, char *name, const char *value)
Definition attr.c:474
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:521
char * agget(void *obj, char *name)
Definition attr.c:447
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
#define ED_dist(e)
Definition types.h:602
#define ED_xlabel(e)
Definition types.h:590
#define ED_head_label(e)
Definition types.h:587
#define agfindedgeattr(g, a)
Definition types.h:617
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:982
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:98
#define ED_tail_label(e)
Definition types.h:596
#define ED_factor(e)
Definition types.h:585
#define aghead(e)
Definition cgraph.h:983
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:89
#define ED_label(e)
Definition types.h:589
void agwarningf(const char *fmt,...)
Definition agerror.c:175
void agerrorf(const char *fmt,...)
Definition agerror.c:167
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:157
@ AGPREV
Definition cgraph.h:951
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_drawing(g)
Definition types.h:353
#define GD_clust(g)
Definition types.h:360
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_ndim(g)
Definition types.h:390
#define GD_label(g)
Definition types.h:374
#define GD_dist(g)
Definition types.h:357
#define GD_flip(g)
Definition types.h:378
#define GD_neato_nlist(g)
Definition types.h:392
#define GD_odim(g)
Definition types.h:391
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
#define ND_pinned(n)
Definition types.h:519
#define agfindnodeattr(g, a)
Definition types.h:615
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_xlabel(n)
Definition types.h:503
#define ND_pos(n)
Definition types.h:520
#define ND_coord(n)
Definition types.h:490
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:22
Agraph_t * agroot(void *obj)
Definition obj.c:170
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGEDGE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
int agdelrec(void *obj, const char *name)
deletes a named record from one object
Definition rec.c:139
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:72
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:77
Agraph_t * graph(char *name)
Definition gv.cpp:34
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static bool gv_isalpha(int c)
Definition gv_ctype.h:29
static bool gv_isspace(int c)
Definition gv_ctype.h:55
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:137
static opts_t opts
Definition gvgen.c:415
static int z
textitem scanner parser str
Definition htmlparse.y:218
void do_graph_label(graph_t *sg)
Set characteristics of graph label if it exists.
Definition input.c:830
#define ITOS(i)
Definition itos.h:43
DistType ** compute_apsp_artificial_weights(vtx_data *graph, int n)
Definition kkutils.c:93
void free_label(textlabel_t *p)
Definition labels.c:204
#define ND_onstack(n)
Definition acyclic.c:31
#define ND_mark(n)
Definition acyclic.c:30
void acyclic(graph_t *g)
Definition acyclic.c:58
Agraph_t ** pccomps(Agraph_t *g, size_t *ncc, char *pfx, bool *pinned)
Definition ccomps.c:123
#define hasPos(n)
Definition macros.h:18
std::unordered_map< std::pair< int, int >, int, PointHash > PointMap
#define ND_id(n)
Definition mm2gv.c:41
static const int dim
#define MODE_HIER
Definition neato.h:23
#define INIT_SELF
Definition neato.h:28
#define MODE_MAJOR
Definition neato.h:22
#define MODE_IPSEP
Definition neato.h:24
#define MODE_SGD
Definition neato.h:25
#define MODE_KK
Definition neato.h:21
#define INIT_RANDOM
Definition neato.h:30
#define MODEL_SUBSET
Definition neato.h:18
#define MODEL_MDS
Definition neato.h:19
#define MODEL_CIRCUIT
Definition neato.h:17
#define INIT_REGULAR
Definition neato.h:29
#define MODEL_SHORTPATH
Definition neato.h:16
static void add_cluster(Agraph_t *g, Agraph_t *subg)
Definition neatoinit.c:434
static void kkNeato(Agraph_t *g, int nG, int model)
solve using gradient descent a la Kamada-Kawai
Definition neatoinit.c:1241
pos_edge
Definition neatoinit.c:359
@ SomeEdges
Definition neatoinit.c:359
@ NoEdges
Definition neatoinit.c:359
@ AllEdges
Definition neatoinit.c:359
#define srand48
Definition neatoinit.c:51
static void neato_init_graph(Agraph_t *g)
Definition neatoinit.c:592
bool user_pos(attrsym_t *posptr, attrsym_t *pinptr, node_t *np, int nG)
Definition neatoinit.c:75
static vtx_data * makeGraphData(graph_t *g, int nv, int *nedges, int mode, int model, node_t ***nodedata)
Definition neatoinit.c:747
static int checkEdge(PointMap *pm, edge_t *ep, int idx)
Definition neatoinit.c:665
#define RANDOM
Definition neatoinit.c:908
int init_nop(Agraph_t *g, int adjust)
Definition neatoinit.c:511
static void neato_init_edge(edge_t *e)
Definition neatoinit.c:68
static void majorization(graph_t *mg, graph_t *g, int nv, int mode, int model, int dim, adjust_data *am)
Definition neatoinit.c:1077
static void initRegular(graph_t *G, int nG)
Definition neatoinit.c:888
static void mds_model(graph_t *g)
Definition neatoinit.c:1223
static void neatoLayout(Agraph_t *mg, Agraph_t *g, int layoutMode, int layoutModel, adjust_data *am)
use stress optimization to layout a single component
Definition neatoinit.c:1274
static void neato_cleanup_graph(graph_t *g)
Definition neatoinit.c:148
#define REGULAR
Definition neatoinit.c:907
static void dfs(Agraph_t *subg, Agraph_t *parentg, attrsym_t *G_lp, attrsym_t *G_bb)
Definition neatoinit.c:454
static pos_edge nop_init_edges(Agraph_t *g)
Definition neatoinit.c:365
static void nop_init_graphs(Agraph_t *, attrsym_t *, attrsym_t *)
Definition neatoinit.c:476
static int neatoModel(graph_t *g)
Definition neatoinit.c:604
static void freeEdgeInfo(Agraph_t *g)
Definition neatoinit.c:393
#define SMART
Definition neatoinit.c:906
static int Pack
Definition neatoinit.c:55
static attrsym_t * N_pos
Definition neatoinit.c:54
void neato_cleanup(graph_t *g)
Definition neatoinit.c:156
static size_t numFields(const char *pos)
Definition neatoinit.c:170
static int checkExp(graph_t *G)
Definition neatoinit.c:959
#define SLEN(s)
Definition neatoinit.c:905
int checkStart(graph_t *G, int nG, int dflt)
Definition neatoinit.c:978
static void set_label(void *obj, textlabel_t *l, char *name)
Definition neatoinit.c:186
void neato_layout(Agraph_t *g)
Definition neatoinit.c:1341
int setSeed(graph_t *G, int dflt, long *seedp)
Definition neatoinit.c:919
#define exp_name
Definition neatoinit.c:957
static int user_spline(attrsym_t *E_pos, edge_t *e)
Definition neatoinit.c:263
static int chkBB(Agraph_t *g, attrsym_t *G_bb, boxf *bbp)
Definition neatoinit.c:413
static int neatoMode(graph_t *g)
Definition neatoinit.c:632
static void addZ(Agraph_t *g)
Definition neatoinit.c:1303
void neato_init_node(node_t *n)
Definition neatoinit.c:60
static char * cc_pfx
Definition neatoinit.c:58
static void subset_model(Agraph_t *G, int nG)
Definition neatoinit.c:1203
static void neato_init_node_edge(graph_t *g)
Definition neatoinit.c:128
NEATOPROCS_API void spline_edges(Agraph_t *)
NEATOPROCS_API void neato_translate(Agraph_t *g)
NEATOPROCS_API void spline_edges0(Agraph_t *, bool)
NEATOPROCS_API void free_scan_graph(graph_t *)
Definition stuff.c:286
NEATOPROCS_API void solve_model(graph_t *, int)
Definition stuff.c:414
NEATOPROCS_API void initial_positions(graph_t *, int)
Definition stuff.c:318
NEATOPROCS_API void jitter_d(Agnode_t *, int, int)
Definition stuff.c:298
NEATOPROCS_API void diffeq_model(graph_t *, int)
Definition stuff.c:341
NEATOPROCS_API bool neato_set_aspect(graph_t *g)
NEATOPROCS_API void jitter3d(Agnode_t *, int)
Definition stuff.c:305
NEATOPROCS_API int scan_graph(graph_t *)
Definition stuff.c:281
NEATOPROCS_API void shortest_path(graph_t *, int)
Definition stuff.c:634
NEATOPROCS_API int scan_graph_mode(graph_t *G, int mode)
Definition stuff.c:201
pack_mode getPackModeInfo(Agraph_t *g, pack_mode dflt, pack_info *pinfo)
Definition pack.c:1256
int getPack(Agraph_t *g, int not_def, int dflt)
Definition pack.c:1269
int packGraphs(size_t ng, Agraph_t **gs, Agraph_t *root, pack_info *info)
Definition pack.c:1092
support for connected components
pack_mode
Definition pack.h:55
@ l_undef
Definition pack.h:55
@ l_node
Definition pack.h:55
void clearPM(PointMap *ps)
Definition pointset.c:153
int insertPM(PointMap *pm, int x, int y, int value)
Definition pointset.c:163
PointMap * newPM(void)
Definition pointset.c:148
void freePM(PointMap *ps)
Definition pointset.c:158
point containers PointSet and PointMap
void gv_postprocess(Agraph_t *g, int allowTranslation)
Definition postproc.c:599
#define PRISIZE_T
Definition prisize_t.h:25
bezier * new_spline(edge_t *e, size_t sz)
create and attach a new Bézier of size sz to the edge d
Definition splines.c:212
void gv_cleanup_edge(Agedge_t *e)
Definition utils.c:1513
void gv_free_splines(edge_t *e)
Definition utils.c:1503
void gv_cleanup_node(Agnode_t *n)
Definition utils.c:1525
static int nedges
total no. of edges used in routing
Definition routespl.c:32
void sgd(graph_t *G, int model)
Definition sgd.c:139
int DistType
Definition sparsegraph.h:39
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
int stress_majorization_kD_mkernel(vtx_data *graph, int n, double **d_coords, node_t **nodes, int dim, int opts, int model, int maxi)
at present, if any nodes have pos set, smart_ini is false
Definition stress.c:782
#define opt_smart_init
Definition stress.h:30
#define opt_exp_flag
Definition stress.h:31
#define DFLT_ITERATIONS
Definition stress.h:23
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
adjust_mode mode
Definition adjust.h:35
Definition types.h:89
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
double x
Definition adjust.h:42
double y
Definition adjust.h:42
bool doAdd
Definition adjust.h:43
pack_mode mode
Definition pack.h:72
bool doSplines
use splines in constructing graph shape
Definition pack.h:71
bool * fixed
Definition pack.h:73
unsigned int margin
Definition pack.h:70
double x
Definition geom.h:29
double y
Definition geom.h:29
pointf pos
Definition types.h:114
bool set
Definition types.h:123
size_t nedges
no. of neighbors, including self
Definition sparsegraph.h:30
double elapsed_sec(void)
Definition timing.c:23
void start_timer(void)
Definition timing.c:21
@ R_NONE
Definition types.h:215
Definition grammar.c:90