Graphviz 15.1.0~dev.20260613.0511
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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13/* xlayout.c:
14 * Written by Emden R. Gansner
15 *
16 * Layout routine to expand initial layout to accommodate node
17 * sizes.
18 */
19
20#ifdef FIX
21Allow sep to be absolute
22additive(margin of n points) Increase less between tries
23#endif
24
25/* uses PRIVATE interface */
26#define FDP_PRIVATE 1
27#include <fdpgen/dbg.h>
28#include <fdpgen/xlayout.h>
29#include <math.h>
30#include <neatogen/adjust.h>
31#include <util/gv_ctype.h>
32
33#define DFLT_overlap "9:prism" /* default overlap value */
34
35 static xparams xParams = {
36 60, /* numIters */
37 0.0, /* T0 */
38 0.3, /* K */
39 1.5, /* C */
40 0 /* loopcnt */
41 };
43
44static double WD2(Agnode_t *n) {
45 return X_marg.doAdd ? (ND_width(n) / 2.0 + X_marg.x)
46 : (ND_width(n) * X_marg.x / 2.0);
47}
48
49static double HT2(Agnode_t *n) {
50 return X_marg.doAdd ? (ND_height(n) / 2.0 + X_marg.y)
51 : (ND_height(n) * X_marg.y / 2.0);
52}
53
54#ifdef DEBUG
55static void pr2graphs(Agraph_t *g0, Agraph_t *g1) {
56 fprintf(stderr, "%s", agnameof(g0));
57 fprintf(stderr, "(%s)", agnameof(g1));
58}
59#endif
60
61static double RAD(Agnode_t *n) {
62 double w = WD2(n);
63 double h = HT2(n);
64 return hypot(w, h);
65}
66
67/* xinit_params:
68 * Initialize local parameters
69 */
70static double xinit_params(graph_t *g, int n, xparams *xpms) {
71 (void)g;
72
73 xParams.K = xpms->K;
75 xParams.T0 = xpms->T0;
76 xParams.loopcnt = xpms->loopcnt;
77 if (xpms->C > 0.0)
78 xParams.C = xpms->C;
79 const double K2 = xParams.K * xParams.K;
80 if (xParams.T0 == 0.0)
81 xParams.T0 = xParams.K * sqrt(n) / 5;
82#ifdef DEBUG
83 if (Verbose) {
84 prIndent();
85 fprintf(stderr, "xLayout ");
86 pr2graphs(g, GORIG(agroot(g)));
87 fprintf(stderr, " : n = %d K = %f T0 = %f loop %d C %f\n", xParams.numIters,
89 }
90#endif
91 return K2;
92}
93
94#define X_T0 xParams.T0
95#define X_K xParams.K
96#define X_numIters xParams.numIters
97#define X_loopcnt xParams.loopcnt
98#define X_C xParams.C
99
100static double cool(int t) { return X_T0 * (X_numIters - t) / X_numIters; }
101
103static int overlap(node_t *p, node_t *q) {
104 const double xdelta = fabs(ND_pos(q)[0] - ND_pos(p)[0]);
105 const double ydelta = fabs(ND_pos(q)[1] - ND_pos(p)[1]);
106 return xdelta <= WD2(p) + WD2(q) && ydelta <= HT2(p) + HT2(q);
107}
108
110static int cntOverlaps(graph_t *g) {
111 int cnt = 0;
112
113 for (node_t *p = agfstnode(g); p; p = agnxtnode(g, p)) {
114 for (node_t *q = agnxtnode(g, p); q; q = agnxtnode(g, q)) {
115 cnt += overlap(p, q);
116 }
117 }
118 return cnt;
119}
120
122static int doRep(node_t *p, node_t *q, double xdelta, double ydelta,
123 double dist, double X_ov, double X_nonov) {
124 int ov;
125 double force;
126
127 while (!(dist > 0)) {
128 xdelta = 5 - rand() % 10;
129 ydelta = 5 - rand() % 10;
130 dist = hypot(xdelta, ydelta);
131 }
132 if ((ov = overlap(p, q)))
133 force = X_ov / (dist * dist);
134 else
135 force = X_nonov / (dist * dist);
136 if (dist > fdp_parms->Mlimit)
137 force = 0;
138#ifdef DEBUG
139 if (Verbose == 4) {
140 prIndent();
141 fprintf(stderr, " ov Fr %f dist %f\n", force * dist, dist);
142 }
143#endif
144 DISP(q)[0] += xdelta * force;
145 DISP(q)[1] += ydelta * force;
146 DISP(p)[0] -= xdelta * force;
147 DISP(p)[1] -= ydelta * force;
148 return ov;
149}
150
151/* Repulsive force = (K*K)/d
152 * Return 1 if nodes overlap
153 */
154static int applyRep(Agnode_t *p, Agnode_t *q, double X_ov, double X_nonov) {
155 const double xdelta = ND_pos(q)[0] - ND_pos(p)[0];
156 const double ydelta = ND_pos(q)[1] - ND_pos(p)[1];
157 return doRep(p, q, xdelta, ydelta, hypot(xdelta, ydelta), X_ov, X_nonov);
158}
159
160static void applyAttr(Agnode_t *p, Agnode_t *q) {
161 if (overlap(p, q)) {
162#ifdef DEBUG
163 if (Verbose == 4) {
164 prIndent();
165 fprintf(stderr, "ov 1 Fa 0 din %f\n", RAD(p) + RAD(q));
166 }
167#endif
168 return;
169 }
170 const double xdelta = ND_pos(q)[0] - ND_pos(p)[0];
171 const double ydelta = ND_pos(q)[1] - ND_pos(p)[1];
172 const double dist = hypot(xdelta, ydelta);
173 const double din = RAD(p) + RAD(q);
174 const double dout = dist - din;
175 const double force = dout * dout / ((X_K + din) * dist);
176#ifdef DEBUG
177 if (Verbose == 4) {
178 prIndent();
179 fprintf(stderr, " ov 0 Fa %f din %f \n", force * dist, din);
180 }
181#endif
182 DISP(q)[0] -= xdelta * force;
183 DISP(q)[1] -= ydelta * force;
184 DISP(p)[0] += xdelta * force;
185 DISP(p)[1] += ydelta * force;
186}
187
188/* Return 0 if definitely no overlaps.
189 * Return non-zero if we had overlaps before most recent move.
190 */
191static int adjust(Agraph_t *g, double temp, double X_ov, double X_nonov) {
192 int overlaps = 0;
193
194#ifdef DEBUG
195 if (Verbose == 4)
196 fprintf(stderr, "=================\n");
197#endif
198
199 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
200 DISP(n)[0] = DISP(n)[1] = 0;
201 }
202
203 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
204 int ov;
205 for (Agnode_t *n1 = agnxtnode(g, n); n1; n1 = agnxtnode(g, n1)) {
206 ov = applyRep(n, n1, X_ov, X_nonov);
207 overlaps += ov;
208 }
209 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
210 applyAttr(n, aghead(e));
211 }
212 }
213 if (overlaps == 0)
214 return 0;
215
216 const double temp2 = temp * temp;
217 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
218 if (ND_pinned(n) == P_PIN)
219 continue;
220 const double disp[] = {DISP(n)[0], DISP(n)[1]}; // incremental displacement
221 const double len2 = disp[0] * disp[0] + disp[1] * disp[1];
222
223 if (len2 < temp2) {
224 ND_pos(n)[0] += disp[0];
225 ND_pos(n)[1] += disp[1];
226 } else {
227 /* to avoid sqrt, consider abs(x) + abs(y) */
228 const double len = sqrt(len2);
229 ND_pos(n)[0] += disp[0] * temp / len;
230 ND_pos(n)[1] += disp[1] * temp / len;
231 }
232 }
233 return overlaps;
234}
235
236/* Given graph g with initial layout, adjust g so that nodes
237 * do not overlap.
238 * Assume g is connected.
239 * g may have ports. At present, we do not use ports in the layout
240 * at this stage.
241 * Returns non-zero if overlaps still exist.
242 * TODO (possible):
243 * Allow X_T0 independent of T_TO or percentage of, so the cooling would
244 * be piecewise linear. This would allow longer, cooler expansion.
245 * In tries > 1, increase X_T0 and/or lengthen cooling
246 */
247static int x_layout(graph_t *g, xparams *pxpms, int tries) {
248 int nnodes = agnnodes(g);
249 int nedges = agnedges(g);
250
251 X_marg = sepFactor(g);
252 if (X_marg.doAdd) {
253 X_marg.x = PS2INCH(X_marg.x); /* sepFactor is in points */
255 }
256 int ov = cntOverlaps(g);
257 if (ov == 0)
258 return 0;
259
260 xparams xpms = *pxpms;
261 const double K = xpms.K;
262 for (int try = 0; ov && try < tries; ++try) {
263 const double K2 = xinit_params(g, nnodes, &xpms);
264 const double X_ov = X_C * K2;
265 const double X_nonov = nedges * X_ov * 2.0 / (nnodes * (nnodes - 1));
266#ifdef DEBUG
267 if (Verbose) {
268 prIndent();
269 fprintf(stderr, "try %d (%d): %d overlaps on ", try, tries, ov);
270 pr2graphs(g, GORIG(agroot(g)));
271 fprintf(stderr, " \n");
272 }
273#endif
274
275 for (int i = 0; i < X_loopcnt; i++) {
276 const double temp = cool(i);
277 if (temp <= 0.0)
278 break;
279 ov = adjust(g, temp, X_ov, X_nonov);
280 if (ov == 0)
281 break;
282 }
283 xpms.K += K; /* increase distance */
284 }
285#ifdef DEBUG
286 if (Verbose && ov)
287 fprintf(stderr, "Warning: %d overlaps remain on ", ov);
288 pr2graphs(g, GORIG(agroot(g)));
289 fprintf(stderr, "\n");
290#endif
291
292 return ov;
293}
294
295/* Use overlap parameter to determine if and how to remove overlaps.
296 * In addition to the usual values accepted by removeOverlap, overlap
297 * can begin with "n:" to indicate the given number of tries using
298 * x_layout to remove overlaps.
299 * Thus,
300 * NULL or "" => dflt overlap
301 * "mode" => "0:mode", i.e. removeOverlap with mode only
302 * "true" => "0:true", i.e., no overlap removal
303 * "n:" => n tries only
304 * "n:mode" => n tries, then removeOverlap with mode
305 * "0:" => no overlap removal
306 */
307void fdp_xLayout(graph_t *g, xparams *xpms) {
308 int tries;
309 char *ovlp = agget(g, "overlap");
310 char *cp;
311 char *rest;
312
313 if (Verbose) {
314#ifdef DEBUG
315 prIndent();
316#endif
317 fprintf(stderr, "xLayout ");
318 }
319 if (!ovlp || *ovlp == '\0') {
320 ovlp = DFLT_overlap;
321 }
322 /* look for optional ":" or "number:" */
323 if ((cp = strchr(ovlp, ':')) && (cp == ovlp || gv_isdigit(*ovlp))) {
324 cp++;
325 rest = cp;
326 tries = atoi(ovlp);
327 if (tries < 0)
328 tries = 0;
329 } else {
330 tries = 0;
331 rest = ovlp;
332 }
333 if (Verbose) {
334#ifdef DEBUG
335 prIndent();
336#endif
337 fprintf(stderr, "tries = %d, mode = %s\n", tries, rest);
338 }
339 if (tries && !x_layout(g, xpms, tries))
340 return;
341 removeOverlapAs(g, rest);
342}
expand_t sepFactor(graph_t *g)
Definition adjust.c:1046
int removeOverlapAs(graph_t *G, char *flag)
Use flag value to determine if and how to remove node overlaps.
Definition adjust.c:986
#define P_PIN
Definition const.h:249
static int overlaps(nitem *p, int cnt)
Definition constraint.c:465
static const char adjust[]
Definition emit.c:3078
static double dist(int dim, double *x, double *y)
#define PS2INCH(a_points)
Definition geom.h:64
struct fdpParms_s * fdp_parms
Definition globals.c:38
static double len(glCompPoint p)
Definition glutils.c:138
static bool Verbose
Definition gml2gv.c:26
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:200
int agnedges(Agraph_t *g)
Definition graph.c:165
int agnnodes(Agraph_t *g)
Definition graph.c:159
char * agget(void *obj, char *name)
Definition attr.c:447
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
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 ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_pos(n)
Definition types.h:520
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
Agraph_t * agroot(void *obj)
Definition obj.c:170
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:86
static int nedges
total no. of edges used in routing
Definition routespl.c:32
#define HT2(n)
Definition splines.c:375
static const double cool
graph or subgraph
Definition cgraph.h:424
double x
Definition adjust.h:42
double y
Definition adjust.h:42
bool doAdd
Definition adjust.h:43
double Mlimit
distance beyond which repulsive force is 0
Definition fdp.h:104
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 X_T0
Definition xlayout.c:94
#define X_K
Definition xlayout.c:95
#define DFLT_overlap
Definition xlayout.c:33
static int cntOverlaps(graph_t *g)
return number of overlaps
Definition xlayout.c:110
static xparams xParams
Definition xlayout.c:35
static int doRep(node_t *p, node_t *q, double xdelta, double ydelta, double dist, double X_ov, double X_nonov)
return 1 if nodes overlap
Definition xlayout.c:122
static double WD2(Agnode_t *n)
Definition xlayout.c:44
#define X_C
Definition xlayout.c:98
static int x_layout(graph_t *g, xparams *pxpms, int tries)
Definition xlayout.c:247
static double xinit_params(graph_t *g, int n, xparams *xpms)
Definition xlayout.c:70
static double RAD(Agnode_t *n)
Definition xlayout.c:61
static int overlap(node_t *p, node_t *q)
return true if nodes overlap
Definition xlayout.c:103
#define X_numIters
Definition xlayout.c:96
static int applyRep(Agnode_t *p, Agnode_t *q, double X_ov, double X_nonov)
Definition xlayout.c:154
static void applyAttr(Agnode_t *p, Agnode_t *q)
Definition xlayout.c:160
static expand_t X_marg
Definition xlayout.c:42
void fdp_xLayout(graph_t *g, xparams *xpms)
Definition xlayout.c:307
#define X_loopcnt
Definition xlayout.c:97