Graphviz 14.0.5~dev.20251117.1017
Loading...
Searching...
No Matches
constraint.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#include "config.h"
13#include <common/geom.h>
14#include <math.h>
15#include <neatogen/neato.h>
16#include <neatogen/adjust.h>
17#include <stddef.h>
18#include <stdbool.h>
19#include <util/alloc.h>
20#include <util/itos.h>
21#include <util/list.h>
22
23/* For precision, scale up before algorithms, then scale down */
24#define SCALE 10
25#define SCALE2 (SCALE/2)
26
27typedef struct nitem {
29 int val;
30 point pos; /* position for sorting */
31 node_t *np; /* base node */
32 node_t *cnode; /* corresponding node in constraint graph */
33 node_t *vnode; /* corresponding node in neighbor graph */
36
37typedef int (*distfn) (box *, box *);
38typedef int (*intersectfn) (nitem *, nitem *);
39
40static int cmpitem(void *item1, void *item2) {
41 const int *p1 = item1;
42 const int *p2 = item2;
43 if (*p1 < *p2) {
44 return -1;
45 }
46 if (*p1 > *p2) {
47 return 1;
48 }
49 return 0;
50}
51
52static Dtdisc_t constr = {
53 offsetof(nitem, val),
54 sizeof(int),
55 offsetof(nitem, link),
56 NULL,
57 NULL,
58 cmpitem,
59};
60
61static int distY(box * b1, box * b2)
62{
63 return ((b1->UR.y - b1->LL.y) + (b2->UR.y - b2->LL.y)) / 2;
64}
65
66static int distX(box * b1, box * b2)
67{
68 return ((b1->UR.x - b1->LL.x) + (b2->UR.x - b2->LL.x)) / 2;
69}
70
71/* Return true if boxes could overlap if shifted in y but don't,
72 * or if actually overlap and an y move is smallest to remove overlap.
73 * Otherwise (no x overlap or a x move is smaller), return false.
74 * Assume q pos to above of p pos.
75 */
76static int intersectX0(nitem * p, nitem * q)
77{
78 int xdelta, ydelta;
79 int v = p->bb.LL.x <= q->bb.UR.x && q->bb.LL.x <= p->bb.UR.x;
80 if (v == 0) /* no x overlap */
81 return 0;
82 if (p->bb.UR.y < q->bb.LL.y) /* but boxes don't really overlap */
83 return 1;
84 ydelta = distY(&p->bb,&q->bb) - (q->pos.y - p->pos.y);
85 if (q->pos.x >= p->pos.x)
86 xdelta = distX(&p->bb,&q->bb) - (q->pos.x - p->pos.x);
87 else
88 xdelta = distX(&p->bb,&q->bb) - (p->pos.x - q->pos.x);
89 return ydelta <= xdelta;
90}
91
92/* Return true if boxes could overlap if shifted in x but don't,
93 * or if actually overlap and an x move is smallest to remove overlap.
94 * Otherwise (no y overlap or a y move is smaller), return false.
95 * Assume q pos to right of p pos.
96 */
97static int intersectY0(nitem * p, nitem * q)
98{
99 int xdelta, ydelta;
100 int v = p->bb.LL.y <= q->bb.UR.y && q->bb.LL.y <= p->bb.UR.y;
101 if (v == 0) /* no y overlap */
102 return 0;
103 if (p->bb.UR.x < q->bb.LL.x) /* but boxes don't really overlap */
104 return 1;
105 xdelta = distX(&p->bb,&q->bb) - (q->pos.x - p->pos.x);
106 if (q->pos.y >= p->pos.y)
107 ydelta = distY(&p->bb,&q->bb) - (q->pos.y - p->pos.y);
108 else
109 ydelta = distY(&p->bb,&q->bb) - (p->pos.y - q->pos.y);
110 return xdelta <= ydelta;
111}
112
113static int intersectY(nitem * p, nitem * q)
114{
115 return p->bb.LL.y <= q->bb.UR.y && q->bb.LL.y <= p->bb.UR.y;
116}
117
118static int intersectX(nitem * p, nitem * q)
119{
120 return p->bb.LL.x <= q->bb.UR.x && q->bb.LL.x <= p->bb.UR.x;
121}
122
123static void mapGraphs(graph_t * g, graph_t * cg, distfn dist)
124{
125 node_t *n;
126 edge_t *e;
127 edge_t *ce;
128 node_t *t;
129 node_t *h;
130 nitem *tp;
131 nitem *hp;
132 int delta;
133
134 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
135 tp = ND_alg(n);
136 t = tp->cnode;
137 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
138 hp = ND_alg(aghead(e));
139 delta = dist(&tp->bb, &hp->bb);
140 h = hp->cnode;
141 ce = agedge(cg, t, h, NULL, 1);
142 agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
143 ED_weight(ce) = 1;
144 if (ED_minlen(ce) < delta) {
145 if (ED_minlen(ce) == 0.0) {
146 elist_append(ce, ND_out(t));
147 elist_append(ce, ND_in(h));
148 }
149 ED_minlen(ce) = delta;
150 }
151 }
152 }
153}
154
155#if defined(DEBUG) && DEBUG > 1
156static int
157indegree (graph_t * g, node_t *n)
158{
159 edge_t *e;
160 int cnt = 0;
161 for (e = agfstin(g,n); e; e = agnxtin(g,e)) cnt++;
162 return cnt;
163}
164
165static int
166outdegree (graph_t * g, node_t *n)
167{
168 edge_t *e;
169 int cnt = 0;
170 for (e = agfstout(g,n); e; e = agnxtout(g,e)) cnt++;
171 return cnt;
172}
173
174static void
175validate(graph_t * g)
176{
177 node_t *n;
178 edge_t *e;
179 int i, cnt;
180
181 cnt = 0;
182 for (n = GD_nlist(g);n; n = ND_next(n)) {
183 assert(outdegree(g,n) == ND_out(n).size);
184 for (i = 0; (e = ND_out(n).list[i]); i++) {
185 assert(agtail(e) == n);
186 assert( e == agfindedge(g, n, aghead(e)));
187 }
188 assert(indegree(g,n) == ND_in(n).size);
189 for (i = 0; (e = ND_in(n).list[i]); i++) {
190 assert(aghead(e) == n);
191 assert( e == agfindedge(g, agtail(e), n));
192 }
193 cnt++;
194 }
195
196 assert (agnnodes(g) == cnt);
197}
198#endif
199
200
201/* Similar to mkConstraintG, except it doesn't enforce orthogonal
202 * ordering. If there is overlap, as defined by intersect, the
203 * nodes will kept/pushed apart in the current order. If not, no
204 * constraint is enforced. If a constraint edge is added, and it
205 * corresponds to a real edge, we increase the weight in an attempt
206 * to keep the resulting shift short.
207 */
208static graph_t *mkNConstraintG(graph_t * g, Dt_t * list,
210{
211 nitem *p;
212 nitem *nxp;
213 node_t *n;
214 edge_t *e;
215 node_t *lastn = NULL;
217 agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); // graph custom data
218
219 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
220 n = agnode(cg, agnameof(p->np), 1); /* FIX */
221 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
222 ND_alg(n) = p;
223 p->cnode = n;
224 alloc_elist(0, ND_in(n));
225 alloc_elist(0, ND_out(n));
226 if (lastn) {
227 ND_next(lastn) = n;
228 lastn = n;
229 } else {
230 lastn = GD_nlist(cg) = n;
231 }
232 }
233 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
234 for (nxp = (nitem *)dtlink(link, p); nxp; nxp = (nitem *)dtlink(list, nxp)) {
235 e = NULL;
236 if (intersect(p, nxp)) {
237 double delta = dist(&p->bb, &nxp->bb);
238 e = agedge(cg, p->cnode, nxp->cnode, NULL, 1);
239 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); // edge custom data
240 assert (delta <= 0xFFFF);
241 ED_minlen(e) = delta;
242 ED_weight(e) = 1;
243 }
244 if (e && agfindedge(g,p->np, nxp->np)) {
245 ED_weight(e) = 100;
246 }
247 }
248 }
249
250 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
251 n = p->cnode;
252 for (e = agfstout(cg,n); e; e = agnxtout(cg,e)) {
253 elist_append(e, ND_out(n));
254 elist_append(e, ND_in(aghead(e)));
255 }
256 }
257
258 /* We could remove redundant constraints here. However, the cost of doing
259 * this may be a good deal more than the time saved in network simplex.
260 * Also, if the graph is changed, the ND_in and ND_out data has to be
261 * updated.
262 */
263 return cg;
264}
265
267 nitem *p;
268 nitem *nxt = NULL;
269 nitem *nxp;
270 graph_t *vg;
271 node_t *prev = NULL;
272 node_t *root = NULL;
273 node_t *n = NULL;
274 edge_t *e;
275 int lcnt, cnt;
276 int oldval = -INT_MAX;
277 node_t *lastn = NULL;
279 agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); // graph custom data
280
281 /* count distinct nodes */
282 cnt = 0;
283 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
284 if (oldval != p->val) {
285 oldval = p->val;
286 cnt++;
287 }
288 }
289
290 /* construct basic chain to enforce left to right order */
291 oldval = -INT_MAX;
292 lcnt = 0;
293 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
294 if (oldval != p->val) {
295 oldval = p->val;
296 /* n = newNode (cg); */
297 n = agnode(cg, agnameof(p->np), 1); /* FIX */
298 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
299 ND_alg(n) = p;
300 if (root) {
301 ND_next(lastn) = n;
302 lastn = n;
303 } else {
304 root = n;
305 lastn = GD_nlist(cg) = n;
306 }
307 alloc_elist(lcnt, ND_in(n));
308 if (prev) {
309 if (prev == root)
310 alloc_elist(2 * (cnt - 1), ND_out(prev));
311 else
312 alloc_elist(cnt - lcnt - 1, ND_out(prev));
313 e = agedge(cg, prev, n, NULL, 1);
314 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); // edge custom data
315 ED_minlen(e) = SCALE;
316 ED_weight(e) = 1;
318 elist_append(e, ND_in(n));
319 }
320 lcnt++;
321 prev = n;
322 }
323 p->cnode = n;
324 }
326
327 /* add immediate right neighbor constraints
328 * Construct visibility graph, then perform transitive reduction.
329 * Remaining outedges are immediate right neighbors.
330 * FIX: Incremental algorithm to construct trans. reduction?
331 */
332 vg = agopen("vg", Agstrictdirected, NULL);
333 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
334 n = agnode(vg, agnameof(p->np), 1); /* FIX */
335 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
336 p->vnode = n;
337 ND_alg(n) = p;
338 }
339 oldval = -INT_MAX;
340 for (p = (nitem *)dtflatten(list); p; p = (nitem *)dtlink(list, p)) {
341 if (oldval != p->val) { /* new pos: reset nxt */
342 oldval = p->val;
343 for (nxt = (nitem *)dtlink(link, p); nxt;
344 nxt = (nitem *)dtlink(list, nxt)) {
345 if (nxt->val != oldval)
346 break;
347 }
348 if (!nxt)
349 break;
350 }
351 for (nxp = nxt; nxp; nxp = (nitem *)dtlink(list, nxp)) {
352 if (intersect(p, nxp))
353 agedge(vg, p->vnode, nxp->vnode, NULL, 1);
354 }
355 }
356
357 /* Remove redundant constraints here. However, the cost of doing this
358 * may be a good deal more than the time saved in network simplex. Also,
359 * if the graph is changed, the ND_in and ND_out data has to be updated.
360 */
361 mapGraphs(vg, cg, dist);
362 agclose(vg);
363
364 return cg;
365}
366
367static void closeGraph(graph_t * cg)
368{
369 for (node_t *n = agfstnode(cg); n; n = agnxtnode(cg, n)) {
370 free_list(ND_in(n));
371 free_list(ND_out(n));
372 }
373 agclose(cg);
374}
375
376/* Create the X constrains and solve. We use a linear objective function
377 * (absolute values rather than squares), so we can reuse network simplex.
378 * The constraints are encoded as a dag with edges having a minimum length.
379 */
380static void constrainX(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn,
381 int ortho)
382{
383 Dt_t *list = dtopen(&constr, Dtobag);
384 nitem *p = nlist;
385 graph_t *cg;
386 int i;
387
388 for (i = 0; i < nnodes; i++) {
389 p->val = p->pos.x;
390 dtinsert(list, p);
391 p++;
392 }
393 if (ortho)
394 cg = mkConstraintG(list, ifn, distX);
395 else
396 cg = mkNConstraintG(g, list, ifn, distX);
397 rank(cg, 2, INT_MAX);
398
399 p = nlist;
400 for (i = 0; i < nnodes; i++) {
401 int newpos, oldpos, delta;
402 oldpos = p->pos.x;
403 newpos = ND_rank(p->cnode);
404 delta = newpos - oldpos;
405 p->pos.x = newpos;
406 p->bb.LL.x += delta;
407 p->bb.UR.x += delta;
408 p++;
409 }
410
411 closeGraph(cg);
412 dtclose(list);
413}
414
416static void constrainY(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn,
417 int ortho)
418{
419 Dt_t *list = dtopen(&constr, Dtobag);
420 nitem *p = nlist;
421 graph_t *cg;
422 int i;
423
424 for (i = 0; i < nnodes; i++) {
425 p->val = p->pos.y;
426 dtinsert(list, p);
427 p++;
428 }
429 if (ortho)
430 cg = mkConstraintG(list, ifn, distY);
431 else
432 cg = mkNConstraintG(g, list, ifn, distY);
433 rank(cg, 2, INT_MAX);
434#ifdef DEBUG
435 {
436 Agsym_t *mlsym = agattr_text(cg, AGEDGE, "minlen", "");
437 Agsym_t *rksym = agattr_text(cg, AGNODE, "rank", "");
438 node_t *n;
439 edge_t *e;
440 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) {
441 agxset(n, rksym, ITOS(ND_rank(n)));
442 for (e = agfstedge(cg, n); e; e = agnxtedge(cg, e, n)) {
443 agxset(e, mlsym, ITOS(ED_minlen(e)));
444 }
445 }
446 }
447#endif
448
449 p = nlist;
450 for (i = 0; i < nnodes; i++) {
451 int newpos, oldpos, delta;
452 oldpos = p->pos.y;
453 newpos = ND_rank(p->cnode);
454 delta = newpos - oldpos;
455 p->pos.y = newpos;
456 p->bb.LL.y += delta;
457 p->bb.UR.y += delta;
458 p++;
459 }
460
461 closeGraph(cg);
462 dtclose(list);
463}
464
465static int overlaps(nitem * p, int cnt)
466{
467 int i, j;
468 nitem *pi = p;
469 nitem *pj;
470
471 for (i = 0; i < cnt - 1; i++) {
472 pj = pi + 1;
473 for (j = i + 1; j < cnt; j++) {
474 if (OVERLAP(pi->bb, pj->bb))
475 return 1;
476 pj++;
477 }
478 pi++;
479 }
480 return 0;
481}
482
483static void initItem(node_t * n, nitem * p, expand_t margin)
484{
485 int x = POINTS(SCALE * ND_pos(n)[0]);
486 int y = POINTS(SCALE * ND_pos(n)[1]);
487 int w2, h2;
488 box b;
489
490 if (margin.doAdd) {
491 w2 = SCALE * (POINTS(ND_width(n)/2.0) + margin.x);
492 h2 = SCALE * (POINTS(ND_height(n)/2.0) + margin.y);
493 }
494 else {
495 w2 = POINTS(margin.x * SCALE2 * ND_width(n));
496 h2 = POINTS(margin.y * SCALE2 * ND_height(n));
497 }
498
499 b.LL.x = x - w2;
500 b.LL.y = y - h2;
501 b.UR.x = x + w2;
502 b.UR.y = y + h2;
503
504 p->pos.x = x;
505 p->pos.y = y;
506 p->np = n;
507 p->bb = b;
508}
509
510/* Use optimization to remove overlaps.
511 * Modifications;
512 * - do y;x then x;y and use the better one
513 * - for all overlaps (or if overlap with leftmost nodes), add a constraint;
514 * constraint could move both x and y away, or the smallest, or some
515 * mixture.
516 * - follow by a scale down using actual shapes
517 * We use an optimization based on Marriott, Stuckey, Tam and He,
518 * "Removing Node Overlapping in Graph Layout Using Constrained Optimization",
519 * Constraints,8(2):143--172, 2003.
520 * We solve 2 constraint problem, one in X, one in Y. In each dimension,
521 * we require relative positions to remain the same. That is, if two nodes
522 * have the same x originally, they have the same x at the end, and if one
523 * node is to the left of another, it remains to the left. In addition, if
524 * two nodes could overlap by moving their X coordinates, we insert a constraint
525 * to keep the two nodes sufficiently apart. Similarly, for Y.
526 *
527 * mode = AM_ORTHOXY => first X, then Y
528 * mode = AM_ORTHOYX => first Y, then X
529 * mode = AM_ORTHO => first X, then Y
530 * mode = AM_ORTHO_YX => first Y, then X
531 * In the last 2 cases, relax the constraints as follows: during the X pass,
532 * if two nodes actually intersect and a smaller move in the Y direction
533 * will remove the overlap, we don't force the nodes apart in the X direction,
534 * but leave it for the Y pass to remove any remaining overlaps. Without this,
535 * the X pass will remove all overlaps, and the Y pass only compresses in the
536 * Y direction, causing a skewing of the aspect ratio.
537 */
538int cAdjust(graph_t * g, int mode)
539{
540 expand_t margin;
541 int ret, i, nnodes = agnnodes(g);
542 nitem *nlist = gv_calloc(nnodes, sizeof(nitem));
543 nitem *p = nlist;
544 node_t *n;
545
546 margin = sepFactor (g);
547
548 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
549 initItem(n, p, margin);
550 p++;
551 }
552
553 if (overlaps(nlist, nnodes)) {
554 point pt;
555
556 switch ((adjust_mode)mode) {
557 case AM_ORTHOXY:
558 constrainX(g, nlist, nnodes, intersectY, 1);
559 constrainY(g, nlist, nnodes, intersectX, 1);
560 break;
561 case AM_ORTHOYX:
562 constrainY(g, nlist, nnodes, intersectX, 1);
563 constrainX(g, nlist, nnodes, intersectY, 1);
564 break;
565 case AM_ORTHO :
566 constrainX(g, nlist, nnodes, intersectY0, 1);
567 constrainY(g, nlist, nnodes, intersectX, 1);
568 break;
569 case AM_ORTHO_YX :
570 constrainY(g, nlist, nnodes, intersectX0, 1);
571 constrainX(g, nlist, nnodes, intersectY, 1);
572 break;
573 case AM_PORTHOXY:
574 constrainX(g, nlist, nnodes, intersectY, 0);
575 constrainY(g, nlist, nnodes, intersectX, 0);
576 break;
577 case AM_PORTHOYX:
578 constrainY(g, nlist, nnodes, intersectX, 0);
579 constrainX(g, nlist, nnodes, intersectY, 0);
580 break;
581 case AM_PORTHO_YX :
582 constrainY(g, nlist, nnodes, intersectX0, 0);
583 constrainX(g, nlist, nnodes, intersectY, 0);
584 break;
585 case AM_PORTHO :
586 default :
587 constrainX(g, nlist, nnodes, intersectY0, 0);
588 constrainY(g, nlist, nnodes, intersectX, 0);
589 break;
590 }
591 p = nlist;
592 for (i = 0; i < nnodes; i++) {
593 n = p->np;
594 pt = p->pos;
595 ND_pos(n)[0] = PS2INCH(pt.x) / SCALE;
596 ND_pos(n)[1] = PS2INCH(pt.y) / SCALE;
597 p++;
598 }
599 ret = 1;
600 }
601 else ret = 0;
602 free(nlist);
603 return ret;
604}
605
606typedef struct {
607 pointf pos; /* position for sorting */
609 double wd2;
610 double ht2;
612} info;
613
614static int sortf(const void *x, const void *y) {
615 const pointf *p = x;
616 const pointf *q = y;
617 if (p->x < q->x)
618 return -1;
619 else if (p->x > q->x)
620 return 1;
621 else if (p->y < q->y)
622 return -1;
623 else if (p->y > q->y)
624 return 1;
625 else
626 return 0;
627}
628
629static double compress(info * nl, int nn)
630{
631 info *p = nl;
632 info *q;
633 int i, j;
634 double s, sc = 0;
635 pointf pt;
636
637 for (i = 0; i < nn; i++) {
638 q = p + 1;
639 for (j = i + 1; j < nn; j++) {
640 if (OVERLAP(p->bb, q->bb))
641 return 0;
642 if (p->pos.x == q->pos.x)
643 pt.x = HUGE_VAL;
644 else {
645 pt.x = (p->wd2 + q->wd2) / fabs(p->pos.x - q->pos.x);
646 }
647 if (p->pos.y == q->pos.y)
648 pt.y = HUGE_VAL;
649 else {
650 pt.y = (p->ht2 + q->ht2) / fabs(p->pos.y - q->pos.y);
651 }
652 if (pt.y < pt.x)
653 s = pt.y;
654 else
655 s = pt.x;
656 if (s > sc)
657 sc = s;
658 q++;
659 }
660 p++;
661 }
662 return sc;
663}
664
665static pointf *mkOverlapSet(info *nl, size_t nn, size_t *cntp) {
666 info *p = nl;
667 info *q;
668 LIST(pointf) S = {0};
669
670 LIST_APPEND(&S, (pointf){0});
671
672 for (size_t i = 0; i < nn; i++) {
673 q = p + 1;
674 for (size_t j = i + 1; j < nn; j++) {
675 if (OVERLAP(p->bb, q->bb)) {
676 pointf pt;
677 if (p->pos.x == q->pos.x)
678 pt.x = HUGE_VAL;
679 else {
680 pt.x = (p->wd2 + q->wd2) / fabs(p->pos.x - q->pos.x);
681 if (pt.x < 1)
682 pt.x = 1;
683 }
684 if (p->pos.y == q->pos.y)
685 pt.y = HUGE_VAL;
686 else {
687 pt.y = (p->ht2 + q->ht2) / fabs(p->pos.y - q->pos.y);
688 if (pt.y < 1)
689 pt.y = 1;
690 }
691 LIST_APPEND(&S, pt);
692 }
693 q++;
694 }
695 p++;
696 }
697
699 pointf *ret;
700 LIST_DETACH(&S, &ret, cntp);
701 return ret;
702}
703
704static pointf computeScaleXY(pointf *aarr, size_t m) {
705 double cost, bestcost;
707
708 aarr[0].x = 1;
709 aarr[0].y = HUGE_VAL;
710 qsort(aarr + 1, m - 1, sizeof(pointf), sortf);
711
712 pointf *barr = gv_calloc(m, sizeof(pointf));
713 barr[m - 1].x = aarr[m - 1].x;
714 barr[m - 1].y = 1;
715 for (size_t k = m - 2; m > 1; k--) {
716 barr[k].x = aarr[k].x;
717 barr[k].y = fmax(aarr[k + 1].y, barr[k + 1].y);
718 if (k == 0) {
719 break;
720 }
721 }
722
723 size_t best = 0;
724 bestcost = HUGE_VAL;
725 for (size_t k = 0; k < m; k++) {
726 cost = barr[k].x * barr[k].y;
727 if (cost < bestcost) {
728 bestcost = cost;
729 best = k;
730 }
731 }
732 assert(bestcost < HUGE_VAL);
733 scale.x = barr[best].x;
734 scale.y = barr[best].y;
735
736 free(barr);
737 return scale;
738}
739
740/* For each (x,y) in aarr, scale has to be bigger than the smallest one.
741 * So, the scale is the max min.
742 */
743static double computeScale(pointf *aarr, size_t m) {
744 double sc = 0;
745 double v;
746 pointf p;
747
748 aarr++;
749 for (size_t i = 1; i < m; i++) {
750 p = *aarr++;
751 v = fmin(p.x, p.y);
752 if (v > sc)
753 sc = v;
754 }
755 return sc;
756}
757
758/* Scale the layout.
759 * equal > 0 => scale uniformly in x and y to remove overlaps
760 * equal = 0 => scale separately in x and y to remove overlaps
761 * equal < 0 => scale down uniformly in x and y to remove excess space
762 * The last assumes there are no overlaps at present.
763 * Based on Marriott, Stuckey, Tam and He,
764 * "Removing Node Overlapping in Graph Layout Using Constrained Optimization",
765 * Constraints,8(2):143--172, 2003.
766 */
767int scAdjust(graph_t * g, int equal)
768{
769 int nnodes = agnnodes(g);
770 info *nlist = gv_calloc(nnodes, sizeof(info));
771 info *p = nlist;
772 node_t *n;
773 pointf s;
774 int i;
775 expand_t margin;
776 pointf *aarr;
777
778 margin = sepFactor (g);
779 if (margin.doAdd) {
780 /* we use inches below */
781 margin.x = PS2INCH(margin.x);
782 margin.y = PS2INCH(margin.y);
783 }
784
785 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
786 double w2, h2;
787 if (margin.doAdd) {
788 w2 = ND_width(n) / 2.0 + margin.x;
789 h2 = ND_height(n) / 2.0 + margin.y;
790 }
791 else {
792 w2 = margin.x * ND_width(n) / 2.0;
793 h2 = margin.y * ND_height(n) / 2.0;
794 }
795 p->pos.x = ND_pos(n)[0];
796 p->pos.y = ND_pos(n)[1];
797 p->bb.LL.x = p->pos.x - w2;
798 p->bb.LL.y = p->pos.y - h2;
799 p->bb.UR.x = p->pos.x + w2;
800 p->bb.UR.y = p->pos.y + h2;
801 p->wd2 = w2;
802 p->ht2 = h2;
803 p->np = n;
804 p++;
805 }
806
807 if (equal < 0) {
808 s.x = s.y = compress(nlist, nnodes);
809 if (s.x == 0) { /* overlaps exist */
810 free(nlist);
811 return 0;
812 }
813 if (Verbose) fprintf(stderr, "compress %g \n", s.x);
814 } else {
815 size_t m;
816 assert(nnodes >= 0);
817 aarr = mkOverlapSet(nlist, (size_t)nnodes, &m);
818
819 if (m == 1) { // no overlaps
820 free(aarr);
821 free(nlist);
822 return 0;
823 }
824
825 if (equal) {
826 s.x = s.y = computeScale(aarr, m);
827 } else {
828 s = computeScaleXY(aarr, m);
829 }
830 free(aarr);
831 if (Verbose) fprintf(stderr, "scale by %g,%g \n", s.x, s.y);
832 }
833
834 p = nlist;
835 for (i = 0; i < nnodes; i++) {
836 ND_pos(p->np)[0] = s.x * p->pos.x;
837 ND_pos(p->np)[1] = s.y * p->pos.y;
838 p++;
839 }
840
841 free(nlist);
842 return 1;
843}
static void newpos(Info_t *ip)
Definition adjust.c:328
expand_t sepFactor(graph_t *g)
Definition adjust.c:1045
adjust_mode
Definition adjust.h:25
@ AM_PORTHOYX
Definition adjust.h:29
@ AM_PORTHO
Definition adjust.h:29
@ AM_ORTHO_YX
Definition adjust.h:28
@ AM_ORTHO
Definition adjust.h:28
@ AM_PORTHO_YX
Definition adjust.h:29
@ AM_ORTHOYX
Definition adjust.h:28
@ AM_ORTHOXY
Definition adjust.h:28
@ AM_PORTHOXY
Definition adjust.h:29
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
CDT_API Dtlink_t * dtflatten(Dt_t *)
Definition dtflatten.c:10
CDT_API Dtmethod_t * Dtobag
ordered multiset
Definition dttree.c:305
#define dtlink(d, e)
Definition cdt.h:176
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
static graph_t * mkNConstraintG(graph_t *g, Dt_t *list, intersectfn intersect, distfn dist)
Definition constraint.c:208
#define SCALE
Definition constraint.c:24
static graph_t * mkConstraintG(Dt_t *list, intersectfn intersect, distfn dist)
Definition constraint.c:266
static int intersectY0(nitem *p, nitem *q)
Definition constraint.c:97
int scAdjust(graph_t *g, int equal)
Definition constraint.c:767
int(* intersectfn)(nitem *, nitem *)
Definition constraint.c:38
static void constrainY(graph_t *g, nitem *nlist, int nnodes, intersectfn ifn, int ortho)
see constrainX
Definition constraint.c:416
static int distX(box *b1, box *b2)
Definition constraint.c:66
static void constrainX(graph_t *g, nitem *nlist, int nnodes, intersectfn ifn, int ortho)
Definition constraint.c:380
static void closeGraph(graph_t *cg)
Definition constraint.c:367
static Dtdisc_t constr
Definition constraint.c:52
static pointf computeScaleXY(pointf *aarr, size_t m)
Definition constraint.c:704
static int distY(box *b1, box *b2)
Definition constraint.c:61
static int cmpitem(void *item1, void *item2)
Definition constraint.c:40
static double computeScale(pointf *aarr, size_t m)
Definition constraint.c:743
static int intersectX(nitem *p, nitem *q)
Definition constraint.c:118
static void initItem(node_t *n, nitem *p, expand_t margin)
Definition constraint.c:483
static pointf * mkOverlapSet(info *nl, size_t nn, size_t *cntp)
Definition constraint.c:665
#define SCALE2
Definition constraint.c:25
int(* distfn)(box *, box *)
Definition constraint.c:37
static int intersectX0(nitem *p, nitem *q)
Definition constraint.c:76
static int sortf(const void *x, const void *y)
Definition constraint.c:614
static int overlaps(nitem *p, int cnt)
Definition constraint.c:465
static void mapGraphs(graph_t *g, graph_t *cg, distfn dist)
Definition constraint.c:123
static double compress(info *nl, int nn)
Definition constraint.c:629
int cAdjust(graph_t *g, int mode)
Definition constraint.c:538
static int intersectY(nitem *p, nitem *q)
Definition constraint.c:113
mode
Definition cvtgxl.c:33
#define S
Definition expr.h:72
static double dist(int dim, double *x, double *y)
geometric types and macros (e.g. points and boxes)
#define PS2INCH(a_points)
Definition geom.h:64
#define OVERLAP(b0, b1)
Definition geom.h:48
#define POINTS(a_inches)
Definition geom.h:62
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
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 agnnodes(Agraph_t *g)
Definition graph.c:155
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 agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:522
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:253
#define ED_minlen(e)
Definition types.h:592
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:71
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:26
#define agtail(e)
Definition cgraph.h:977
#define ED_weight(e)
Definition types.h:603
#define agfindedge(g, t, h)
Definition types.h:609
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:96
#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
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:57
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:95
#define GD_nlist(g)
Definition types.h:393
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:271
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:42
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:141
#define ND_rank(n)
Definition types.h:523
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_next(n)
Definition types.h:510
#define ND_alg(n)
Definition types.h:484
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_pos(n)
Definition types.h:520
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
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
$2 prev
Definition htmlparse.y:291
#define ITOS(i)
Definition itos.h:43
type-generic dynamically expanding list
#define LIST_DETACH(list, datap, sizep)
Definition list.h:443
#define LIST(type)
Definition list.h:55
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_SHRINK_TO_FIT(list)
Definition list.h:358
#define delta
Definition maze.c:136
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:984
static double cg(SparseMatrix A, const double *precond, int n, int dim, double *x0, double *rhs, double tol, double maxit)
graph or subgraph
Definition cgraph.h:424
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
Definition geom.h:39
point LL
Definition geom.h:39
point UR
Definition geom.h:39
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition cdt.h:98
double x
Definition adjust.h:41
double y
Definition adjust.h:41
bool doAdd
Definition adjust.h:42
boxf bb
Definition constraint.c:608
double wd2
Definition constraint.c:609
double ht2
Definition constraint.c:610
pointf pos
Definition constraint.c:607
node_t * np
Definition constraint.c:611
node_t * cnode
Definition constraint.c:32
int val
Definition constraint.c:29
node_t * np
Definition constraint.c:31
point pos
Definition constraint.c:30
box bb
Definition constraint.c:34
Dtlink_t link
Definition constraint.c:28
node_t * vnode
Definition constraint.c:33
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
#define free_list(L)
Definition types.h:272
#define elist_append(item, L)
Definition types.h:261
#define alloc_elist(n, L)
Definition types.h:267
Definition grammar.c:90
static bool intersect(Ppoint_t a, Ppoint_t b, Ppoint_t c, Ppoint_t d)
Definition visibility.c:78