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