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