Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
xlayout.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11
12/* xlayout.c:
13 * Written by Emden R. Gansner
14 *
15 * Layout routine to expand initial layout to accommodate node
16 * sizes.
17 */
18
19#ifdef FIX
20Allow sep to be absolute additive (margin of n points)
21Increase less between tries
22#endif
23
24/* uses PRIVATE interface */
25#define FDP_PRIVATE 1
26#include <cgraph/gv_ctype.h>
27#include <fdpgen/xlayout.h>
28#include <neatogen/adjust.h>
29#include <fdpgen/dbg.h>
30#include <math.h>
31
32/* Use bbox based force function */
33/* #define MS */
34/* Use alternate force function */
35/* #define ALT */
36/* Add repulsive force even if nodes don't overlap */
37/* #define ORIG */
38#define BOX /* Use bbox to determine overlap, else use circles */
39
40#define DFLT_overlap "9:prism" /* default overlap value */
41
42#define WD2(n) (X_marg.doAdd ? (ND_width(n)/2.0 + X_marg.x): ND_width(n)*X_marg.x/2.0)
43#define HT2(n) (X_marg.doAdd ? (ND_height(n)/2.0 + X_marg.y): ND_height(n)*X_marg.y/2.0)
44
45static xparams xParams = {
46 60, /* numIters */
47 0.0, /* T0 */
48 0.3, /* K */
49 1.5, /* C */
50 0 /* loopcnt */
51};
52static double K2;
54static double X_nonov;
55static double X_ov;
56
57#ifdef DEBUG
58static void pr2graphs(Agraph_t *g0, Agraph_t *g1) {
59 fprintf(stderr,"%s",agnameof(g0));
60 fprintf(stderr,"(%s)",agnameof(g1));
61}
62#endif
63
64static double RAD(Agnode_t * n)
65{
66 double w = WD2(n);
67 double h = HT2(n);
68 return hypot(w, h);
69}
70
71/* xinit_params:
72 * Initialize local parameters
73 */
74static void xinit_params(graph_t* g, int n, xparams * xpms)
75{
76 (void)g;
77
78 xParams.K = xpms->K;
80 xParams.T0 = xpms->T0;
81 xParams.loopcnt = xpms->loopcnt;
82 if (xpms->C > 0.0)
83 xParams.C = xpms->C;
84 K2 = xParams.K * xParams.K;
85 if (xParams.T0 == 0.0)
86 xParams.T0 = xParams.K * sqrt(n) / 5;
87#ifdef DEBUG
88 if (Verbose) {
89 prIndent();
90 fprintf(stderr, "xLayout ");
91 pr2graphs(g,GORIG(agroot(g)));
92 fprintf(stderr, " : n = %d K = %f T0 = %f loop %d C %f\n",
94 xParams.C);
95 }
96#endif
97}
98
99#define X_T0 xParams.T0
100#define X_K xParams.K
101#define X_numIters xParams.numIters
102#define X_loopcnt xParams.loopcnt
103#define X_C xParams.C
104
105
106static double cool(int t)
107{
108 return X_T0 * (X_numIters - t) / X_numIters;
109}
110
111#define EPSILON 0.01
112
113#ifdef MS
114/* dist:
115 * Distance between two points
116 */
117static double dist(pointf p, pointf q)
118{
119 double dx, dy;
120
121 dx = p.x - q.x;
122 dy = p.y - q.y;
123 return hypot(dx, dy);
124}
125
126/* bBox:
127 * Compute bounding box of point
128 */
129static void bBox(node_t * p, pointf * ll, pointf * ur)
130{
131 double w2 = WD2(p);
132 double h2 = HT2(p);
133
134 ur->x = ND_pos(p)[0] + w2;
135 ur->y = ND_pos(p)[1] + h2;
136 ll->x = ND_pos(p)[0] - w2;
137 ll->y = ND_pos(p)[1] - h2;
138}
139
140/* boxDist:
141 * Return the distance between two boxes; 0 if they overlap
142 */
143static double boxDist(node_t * p, node_t * q)
144{
145 pointf p_ll, p_ur;
146 pointf q_ll, q_ur;
147
148 bBox(p, &p_ll, &p_ur);
149 bBox(q, &q_ll, &q_ur);
150
151 if (q_ll.x > p_ur.x) {
152 if (q_ll.y > p_ur.y) {
153 return dist(p_ur, q_ll);
154 } else if (q_ll.y >= p_ll.y) {
155 return q_ll.x - p_ur.x;
156 } else {
157 if (q_ur.y >= p_ll.y)
158 return q_ll.x - p_ur.x;
159 else {
160 p_ur.y = p_ll.y; /* p_ur is now lower right */
161 q_ll.y = q_ur.y; /* q_ll is now upper left */
162 return dist(p_ur, q_ll);
163 }
164 }
165 } else if (q_ll.x >= p_ll.x) {
166 if (q_ll.y > p_ur.y) {
167 return q_ll.y - p_ur.x;
168 } else if (q_ll.y >= p_ll.y) {
169 return 0.0;
170 } else {
171 if (q_ur.y >= p_ll.y)
172 return 0.0;
173 else
174 return p_ll.y - q_ur.y;
175 }
176 } else {
177 if (q_ll.y > p_ur.y) {
178 if (q_ur.x >= p_ll.x)
179 return q_ll.y - p_ur.y;
180 else {
181 p_ur.x = p_ll.x; /* p_ur is now upper left */
182 q_ll.x = q_ur.x; /* q_ll is now lower right */
183 return dist(p_ur, q_ll);
184 }
185 } else if (q_ll.y >= p_ll.y) {
186 if (q_ur.x >= p_ll.x)
187 return 0.0;
188 else
189 return p_ll.x - q_ur.x;
190 } else {
191 if (q_ur.x >= p_ll.x) {
192 if (q_ur.y >= p_ll.y)
193 return 0.0;
194 else
195 return p_ll.y - q_ur.y;
196 } else {
197 if (q_ur.y >= p_ll.y)
198 return p_ll.x - q_ur.x;
199 else
200 return dist(p_ll, q_ur);
201 }
202 }
203 }
204}
205#endif /* MS */
206
207/* overlap:
208 * Return true if nodes overlap
209 */
210static int overlap(node_t * p, node_t * q)
211{
212#if defined(BOX)
213 double xdelta, ydelta;
214 int ret;
215
216 xdelta = fabs(ND_pos(q)[0] - ND_pos(p)[0]);
217 ydelta = fabs(ND_pos(q)[1] - ND_pos(p)[1]);
218 ret = xdelta <= WD2(p) + WD2(q) && ydelta <= HT2(p) + HT2(q);
219 return ret;
220#else
221 double dist2, xdelta, ydelta;
222 double din;
223
224 din = RAD(p) + RAD(q);
225 xdelta = ND_pos(q)[0] - ND_pos(p)[0];
226 ydelta = ND_pos(q)[1] - ND_pos(p)[1];
227 dist2 = xdelta * xdelta + ydelta * ydelta;
228 return dist2 <= din * din;
229#endif
230}
231
232/* cntOverlaps:
233 * Return number of overlaps.
234 */
235static int cntOverlaps(graph_t * g)
236{
237 node_t *p;
238 node_t *q;
239 int cnt = 0;
240
241 for (p = agfstnode(g); p; p = agnxtnode(g, p)) {
242 for (q = agnxtnode(g, p); q; q = agnxtnode(g, q)) {
243 cnt += overlap(p, q);
244 }
245 }
246 return cnt;
247}
248
249/* doRep:
250 * Return 1 if nodes overlap
251 */
252static int
253doRep(node_t * p, node_t * q, double xdelta, double ydelta, double dist2)
254{
255 int ov;
256 double force;
257#if defined(DEBUG) || defined(MS) || defined(ALT)
258 double dist;
259#endif
260
261 while (dist2 == 0.0) {
262 xdelta = 5 - rand() % 10;
263 ydelta = 5 - rand() % 10;
264 dist2 = xdelta * xdelta + ydelta * ydelta;
265 }
266#if defined(MS)
267 dout = fmax(boxDist(p, q), EPSILON);
268 dist = sqrt(dist2);
269 force = K2 / (dout * dist);
270#elif defined(ALT)
271 force = K2 / dist2;
272 dist = sqrt(dist2);
273 din = RAD(p) + RAD(q);
274 if (ov = overlap(p, q)) {
275 factor = X_C;
276 } else {
277 ov = 0;
278 if (dist <= din + X_K)
279 factor = X_C * (X_K - (dist - din)) / X_K;
280 else
281 factor = 0.0;
282 }
283 force *= factor;
284#elif defined(ORIG)
285 force = K2 / dist2;
286 if ((ov = overlap(p, q)))
287 force *= X_C;
288#else
289 if ((ov = overlap(p, q)))
290 force = X_ov / dist2;
291 else
292 force = X_nonov / dist2;
293#endif
294#ifdef DEBUG
295 if (Verbose == 4) {
296 prIndent();
297 dist = sqrt(dist2);
298 fprintf(stderr, " ov Fr %f dist %f\n", force * dist, dist);
299 }
300#endif
301 DISP(q)[0] += xdelta * force;
302 DISP(q)[1] += ydelta * force;
303 DISP(p)[0] -= xdelta * force;
304 DISP(p)[1] -= ydelta * force;
305 return ov;
306}
307
308/* applyRep:
309 * Repulsive force = (K*K)/d
310 * Return 1 if nodes overlap
311 */
312static int applyRep(Agnode_t * p, Agnode_t * q)
313{
314 double xdelta, ydelta;
315
316 xdelta = ND_pos(q)[0] - ND_pos(p)[0];
317 ydelta = ND_pos(q)[1] - ND_pos(p)[1];
318 return doRep(p, q, xdelta, ydelta, xdelta * xdelta + ydelta * ydelta);
319}
320
321static void applyAttr(Agnode_t * p, Agnode_t * q)
322{
323 double xdelta, ydelta;
324 double force;
325 double dist;
326 double dout;
327 double din;
328
329#if defined(MS)
330 dout = boxDist(p, q);
331 if (dout == 0.0)
332 return;
333 xdelta = ND_pos(q)[0] - ND_pos(p)[0];
334 ydelta = ND_pos(q)[1] - ND_pos(p)[1];
335 dist = hypot(xdelta, ydelta);
336 force = dout * dout / (X_K * dist);
337#elif defined(ALT)
338 xdelta = ND_pos(q)[0] - ND_pos(p)[0];
339 ydelta = ND_pos(q)[1] - ND_pos(p)[1];
340 dist = hypot(xdelta, ydelta);
341 din = RAD(p) + RAD(q);
342 if (dist < X_K + din)
343 return;
344 dout = dist - din;
345 force = dout * dout / ((X_K + din) * dist);
346#else
347 if (overlap(p, q)) {
348#ifdef DEBUG
349 if (Verbose == 4) {
350 prIndent();
351 fprintf(stderr, "ov 1 Fa 0 din %f\n", RAD(p) + RAD(q));
352 }
353#endif
354 return;
355 }
356 xdelta = ND_pos(q)[0] - ND_pos(p)[0];
357 ydelta = ND_pos(q)[1] - ND_pos(p)[1];
358 dist = hypot(xdelta, ydelta);
359 din = RAD(p) + RAD(q);
360 dout = dist - din;
361 force = dout * dout / ((X_K + din) * dist);
362#endif
363#ifdef DEBUG
364 if (Verbose == 4) {
365 prIndent();
366 fprintf(stderr, " ov 0 Fa %f din %f \n", force * dist, din);
367 }
368#endif
369 DISP(q)[0] -= xdelta * force;
370 DISP(q)[1] -= ydelta * force;
371 DISP(p)[0] += xdelta * force;
372 DISP(p)[1] += ydelta * force;
373}
374
375/* adjust:
376 * Return 0 if definitely no overlaps.
377 * Return non-zero if we had overlaps before most recent move.
378 */
379static int adjust(Agraph_t * g, double temp)
380{
381 Agnode_t *n;
382 Agnode_t *n1;
383 Agedge_t *e;
384 double temp2;
385 double len;
386 double len2;
387 double disp[NDIM]; /* incremental displacement */
388 int overlaps = 0;
389
390#ifdef DEBUG
391 if (Verbose == 4)
392 fprintf(stderr, "=================\n");
393#endif
394
395 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
396 DISP(n)[0] = DISP(n)[1] = 0;
397 }
398
399 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
400 int ov;
401 for (n1 = agnxtnode(g, n); n1; n1 = agnxtnode(g, n1)) {
402 ov = applyRep(n, n1);
403 overlaps += ov;
404 }
405 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
406 applyAttr(n,aghead(e));
407 }
408 }
409 if (overlaps == 0)
410 return 0;
411
412 temp2 = temp * temp;
413 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
414 if (ND_pinned(n) == P_PIN)
415 continue;
416 disp[0] = DISP(n)[0];
417 disp[1] = DISP(n)[1];
418 len2 = disp[0] * disp[0] + disp[1] * disp[1];
419
420 if (len2 < temp2) {
421 ND_pos(n)[0] += disp[0];
422 ND_pos(n)[1] += disp[1];
423 } else {
424 /* to avoid sqrt, consider abs(x) + abs(y) */
425 len = sqrt(len2);
426 ND_pos(n)[0] += disp[0] * temp / len;
427 ND_pos(n)[1] += disp[1] * temp / len;
428 }
429 }
430 return overlaps;
431}
432
433/* x_layout:
434 * Given graph g with initial layout, adjust g so that nodes
435 * do not overlap.
436 * Assume g is connected.
437 * g may have ports. At present, we do not use ports in the layout
438 * at this stage.
439 * Returns non-zero if overlaps still exist.
440 * TODO (possible):
441 * Allow X_T0 independent of T_TO or percentage of, so the cooling would
442 * be piecewise linear. This would allow longer, cooler expansion.
443 * In tries > 1, increase X_T0 and/or lengthen cooling
444 */
445static int x_layout(graph_t * g, xparams * pxpms, int tries)
446{
447 int i;
448 int try;
449 int ov;
450 double temp;
451 int nnodes = agnnodes(g);
452 int nedges = agnedges(g);
453 double K;
454 xparams xpms;
455
456 X_marg = sepFactor (g);
457 if (X_marg.doAdd) {
458 X_marg.x = PS2INCH(X_marg.x); /* sepFactor is in points */
460 }
461 ov = cntOverlaps(g);
462 if (ov == 0)
463 return 0;
464
465 try = 0;
466 xpms = *pxpms;
467 K = xpms.K;
468 while (ov && try < tries) {
469 xinit_params(g, nnodes, &xpms);
470 X_ov = X_C * K2;
471 X_nonov = nedges*X_ov*2.0/(nnodes*(nnodes-1));
472#ifdef DEBUG
473 if (Verbose) {
474 prIndent();
475 fprintf(stderr, "try %d (%d): %d overlaps on ", try, tries, ov);
476 pr2graphs(g,GORIG(agroot(g)));
477 fprintf(stderr," \n");
478 }
479#endif
480
481 for (i = 0; i < X_loopcnt; i++) {
482 temp = cool(i);
483 if (temp <= 0.0)
484 break;
485 ov = adjust(g, temp);
486 if (ov == 0)
487 break;
488 }
489 try++;
490 xpms.K += K; /* increase distance */
491 }
492#ifdef DEBUG
493 if (Verbose && ov)
494 fprintf(stderr, "Warning: %d overlaps remain on ", ov);
495 pr2graphs(g,GORIG(agroot(g)));
496 fprintf(stderr,"\n");
497#endif
498
499 return ov;
500}
501
502/* fdp_xLayout:
503 * Use overlap parameter to determine if and how to remove overlaps.
504 * In addition to the usual values accepted by removeOverlap, overlap
505 * can begin with "n:" to indicate the given number of tries using
506 * x_layout to remove overlaps.
507 * Thus,
508 * NULL or "" => dflt overlap
509 * "mode" => "0:mode", i.e. removeOverlap with mode only
510 * "true" => "0:true", i.e., no overlap removal
511 * "n:" => n tries only
512 * "n:mode" => n tries, then removeOverlap with mode
513 * "0:" => no overlap removal
514 */
515void fdp_xLayout(graph_t * g, xparams * xpms)
516{
517 int tries;
518 char* ovlp = agget (g, "overlap");
519 char* cp;
520 char* rest;
521
522 if (Verbose) {
523#ifdef DEBUG
524 prIndent();
525#endif
526 fprintf (stderr, "xLayout ");
527 }
528 if (!ovlp || *ovlp == '\0') {
529 ovlp = DFLT_overlap;
530 }
531 /* look for optional ":" or "number:" */
532 if ((cp = strchr(ovlp, ':')) && (cp == ovlp || gv_isdigit(*ovlp))) {
533 cp++;
534 rest = cp;
535 tries = atoi (ovlp);
536 if (tries < 0) tries = 0;
537 }
538 else {
539 tries = 0;
540 rest = ovlp;
541 }
542 if (Verbose) {
543#ifdef DEBUG
544 prIndent();
545#endif
546 fprintf (stderr, "tries = %d, mode = %s\n", tries, rest);
547 }
548 if (tries && !x_layout(g, xpms, tries))
549 return;
550 removeOverlapAs(g, rest);
551
552}
expand_t sepFactor(graph_t *g)
Definition adjust.c:1074
int removeOverlapAs(graph_t *G, char *flag)
Use flag value to determine if and how to remove node overlaps.
Definition adjust.c:1014
#define P_PIN
Definition const.h:263
static int overlaps(nitem *p, int cnt)
Definition constraint.c:479
static float dy
Definition draw.c:38
static float dx
Definition draw.c:37
static char adjust[]
Definition emit.c:2792
static double dist(int dim, double *x, double *y)
#define PS2INCH(a_points)
Definition geom.h:70
static double len(glCompPoint p)
Definition glutils.c:150
static int Verbose
Definition gml2gv.c:22
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
int agnedges(Agraph_t *g)
Definition graph.c:164
int agnnodes(Agraph_t *g)
Definition graph.c:158
char * agget(void *obj, char *name)
Definition attr.c:442
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_pinned(n)
Definition types.h:519
#define ND_pos(n)
Definition types.h:520
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
Agraph_t * agroot(void *obj)
Definition obj.c:167
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static gdPoint * points
static int between(double f, double g, double h)
Definition legal.c:95
PATHUTIL_API COORD dist2(Ppoint_t, Ppoint_t)
Definition visibility.c:120
static int nedges
total no. of edges used in routing
Definition routespl.c:31
static const double cool
graph or subgraph
Definition cgraph.h:425
double x
Definition adjust.h:39
double y
Definition adjust.h:39
bool doAdd
Definition adjust.h:40
double x
Definition geom.h:29
double y
Definition geom.h:29
int loopcnt
Definition xlayout.h:24
int numIters
Definition xlayout.h:20
double T0
Definition xlayout.h:21
double C
Definition xlayout.h:23
double K
Definition xlayout.h:22
#define EPSILON
Definition xlayout.c:111
static void xinit_params(graph_t *g, int n, xparams *xpms)
Definition xlayout.c:74
static double X_nonov
Definition xlayout.c:54
#define X_T0
Definition xlayout.c:99
#define X_K
Definition xlayout.c:100
static double K2
Definition xlayout.c:52
#define DFLT_overlap
Definition xlayout.c:40
static int cntOverlaps(graph_t *g)
Definition xlayout.c:235
static xparams xParams
Definition xlayout.c:45
#define HT2(n)
Definition xlayout.c:43
#define X_C
Definition xlayout.c:103
static int x_layout(graph_t *g, xparams *pxpms, int tries)
Definition xlayout.c:445
#define WD2(n)
Definition xlayout.c:42
static double RAD(Agnode_t *n)
Definition xlayout.c:64
static int overlap(node_t *p, node_t *q)
Definition xlayout.c:210
#define X_numIters
Definition xlayout.c:101
static int applyRep(Agnode_t *p, Agnode_t *q)
Definition xlayout.c:312
static void applyAttr(Agnode_t *p, Agnode_t *q)
Definition xlayout.c:321
static expand_t X_marg
Definition xlayout.c:53
static int doRep(node_t *p, node_t *q, double xdelta, double ydelta, double dist2)
Definition xlayout.c:253
void fdp_xLayout(graph_t *g, xparams *xpms)
Definition xlayout.c:515
#define X_loopcnt
Definition xlayout.c:102
static double X_ov
Definition xlayout.c:55