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