constraint.c 21 KB

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