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