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