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