postproc.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. /// @file
  2. /// @ingroup common_render
  3. /*************************************************************************
  4. * Copyright (c) 2011 AT&T Intellectual Property
  5. * All rights reserved. This program and the accompanying materials
  6. * are made available under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution, and is available at
  8. * https://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: Details at https://graphviz.org
  11. *************************************************************************/
  12. #include <common/render.h>
  13. #include <label/xlabels.h>
  14. #include <stdbool.h>
  15. #include <stddef.h>
  16. #include <util/agxbuf.h>
  17. #include <util/alloc.h>
  18. #include <util/prisize_t.h>
  19. #include <util/unreachable.h>
  20. static int Rankdir;
  21. static bool Flip;
  22. static pointf Offset;
  23. static void place_flip_graph_label(graph_t * g);
  24. #define M1 \
  25. "/pathbox {\n\
  26. /Y exch %.5g sub def\n\
  27. /X exch %.5g sub def\n\
  28. /y exch %.5g sub def\n\
  29. /x exch %.5g sub def\n\
  30. newpath x y moveto\n\
  31. X y lineto\n\
  32. X Y lineto\n\
  33. x Y lineto\n\
  34. closepath stroke\n \
  35. } def\n\
  36. /dbgstart { gsave %.5g %.5g translate } def\n\
  37. /arrowlength 10 def\n\
  38. /arrowwidth arrowlength 2 div def\n\
  39. /arrowhead {\n\
  40. gsave\n\
  41. rotate\n\
  42. currentpoint\n\
  43. newpath\n\
  44. moveto\n\
  45. arrowlength arrowwidth 2 div rlineto\n\
  46. 0 arrowwidth neg rlineto\n\
  47. closepath fill\n\
  48. grestore\n\
  49. } bind def\n\
  50. /makearrow {\n\
  51. currentpoint exch pop sub exch currentpoint pop sub atan\n\
  52. arrowhead\n\
  53. } bind def\n\
  54. /point {\
  55. newpath\
  56. 2 0 360 arc fill\
  57. } def\
  58. /makevec {\n\
  59. /Y exch def\n\
  60. /X exch def\n\
  61. /y exch def\n\
  62. /x exch def\n\
  63. newpath x y moveto\n\
  64. X Y lineto stroke\n\
  65. X Y moveto\n\
  66. x y makearrow\n\
  67. } def\n"
  68. #define M2 \
  69. "/pathbox {\n\
  70. /X exch neg %.5g sub def\n\
  71. /Y exch %.5g sub def\n\
  72. /x exch neg %.5g sub def\n\
  73. /y exch %.5g sub def\n\
  74. newpath x y moveto\n\
  75. X y lineto\n\
  76. X Y lineto\n\
  77. x Y lineto\n\
  78. closepath stroke\n\
  79. } def\n"
  80. static pointf map_point(pointf p)
  81. {
  82. p = ccwrotatepf(p, Rankdir * 90);
  83. p.x -= Offset.x;
  84. p.y -= Offset.y;
  85. return p;
  86. }
  87. static void map_edge(edge_t * e)
  88. {
  89. bezier bz;
  90. if (ED_spl(e) == NULL) {
  91. if (!Concentrate && ED_edge_type(e) != IGNORED)
  92. agerrorf("lost %s %s edge\n", agnameof(agtail(e)),
  93. agnameof(aghead(e)));
  94. return;
  95. }
  96. for (size_t j = 0; j < ED_spl(e)->size; j++) {
  97. bz = ED_spl(e)->list[j];
  98. for (size_t k = 0; k < bz.size; k++)
  99. bz.list[k] = map_point(bz.list[k]);
  100. if (bz.sflag)
  101. ED_spl(e)->list[j].sp = map_point(ED_spl(e)->list[j].sp);
  102. if (bz.eflag)
  103. ED_spl(e)->list[j].ep = map_point(ED_spl(e)->list[j].ep);
  104. }
  105. if (ED_label(e))
  106. ED_label(e)->pos = map_point(ED_label(e)->pos);
  107. if (ED_xlabel(e))
  108. ED_xlabel(e)->pos = map_point(ED_xlabel(e)->pos);
  109. if (ED_head_label(e))
  110. ED_head_label(e)->pos = map_point(ED_head_label(e)->pos);
  111. if (ED_tail_label(e))
  112. ED_tail_label(e)->pos = map_point(ED_tail_label(e)->pos);
  113. }
  114. void translate_bb(graph_t * g, int rankdir)
  115. {
  116. int c;
  117. boxf bb, new_bb;
  118. bb = GD_bb(g);
  119. if (rankdir == RANKDIR_LR || rankdir == RANKDIR_BT) {
  120. new_bb.LL = map_point((pointf){bb.LL.x, bb.UR.y});
  121. new_bb.UR = map_point((pointf){bb.UR.x, bb.LL.y});
  122. } else {
  123. new_bb.LL = map_point((pointf){bb.LL.x, bb.LL.y});
  124. new_bb.UR = map_point((pointf){bb.UR.x, bb.UR.y});
  125. }
  126. GD_bb(g) = new_bb;
  127. if (GD_label(g)) {
  128. GD_label(g)->pos = map_point(GD_label(g)->pos);
  129. }
  130. for (c = 1; c <= GD_n_cluster(g); c++)
  131. translate_bb(GD_clust(g)[c], rankdir);
  132. }
  133. /* translate_drawing:
  134. * Translate and/or rotate nodes, spline points, and bbox info if
  135. * necessary. Also, if Rankdir (!= RANKDIR_BT), reset ND_lw, ND_rw,
  136. * and ND_ht to correct value.
  137. */
  138. static void translate_drawing(graph_t * g)
  139. {
  140. node_t *v;
  141. edge_t *e;
  142. bool shift = Offset.x || Offset.y;
  143. if (!shift && !Rankdir)
  144. return;
  145. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  146. if (Rankdir)
  147. gv_nodesize(v, false);
  148. ND_coord(v) = map_point(ND_coord(v));
  149. if (ND_xlabel(v))
  150. ND_xlabel(v)->pos = map_point(ND_xlabel(v)->pos);
  151. if (State == GVSPLINES)
  152. for (e = agfstout(g, v); e; e = agnxtout(g, e))
  153. map_edge(e);
  154. }
  155. translate_bb(g, GD_rankdir(g));
  156. }
  157. /* place_root_label:
  158. * Set position of root graph label.
  159. * Note that at this point, after translate_drawing, a
  160. * flipped drawing has been transposed, so we don't have
  161. * to worry about switching x and y.
  162. */
  163. static void place_root_label(graph_t * g, pointf d)
  164. {
  165. pointf p;
  166. if (GD_label_pos(g) & LABEL_AT_RIGHT) {
  167. p.x = GD_bb(g).UR.x - d.x / 2;
  168. } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
  169. p.x = GD_bb(g).LL.x + d.x / 2;
  170. } else {
  171. p.x = (GD_bb(g).LL.x + GD_bb(g).UR.x) / 2;
  172. }
  173. if (GD_label_pos(g) & LABEL_AT_TOP) {
  174. p.y = GD_bb(g).UR.y - d.y / 2;
  175. } else {
  176. p.y = GD_bb(g).LL.y + d.y / 2;
  177. }
  178. GD_label(g)->pos = p;
  179. GD_label(g)->set = true;
  180. }
  181. /* centerPt:
  182. * Calculate the center point of the xlabel. The returned positions for
  183. * xlabels always correspond to the lower left corner.
  184. */
  185. static pointf
  186. centerPt (xlabel_t* xlp) {
  187. pointf p;
  188. p = xlp->pos;
  189. p.x += xlp->sz.x / 2.0;
  190. p.y += xlp->sz.y / 2.0;
  191. return p;
  192. }
  193. static void printData(object_t *objs, size_t n_objs, xlabel_t *lbls,
  194. size_t n_lbls, label_params_t *params) {
  195. xlabel_t* xp;
  196. fprintf (stderr, "%" PRISIZE_T " objs %" PRISIZE_T
  197. " xlabels force=%d bb=(%.02f,%.02f) (%.02f,%.02f)\n",
  198. n_objs, n_lbls, params->force, params->bb.LL.x, params->bb.LL.y,
  199. params->bb.UR.x, params->bb.UR.y);
  200. if (Verbose < 2) return;
  201. fprintf(stderr, "objects\n");
  202. for (size_t i = 0; i < n_objs; i++) {
  203. xp = objs->lbl;
  204. fprintf(stderr, " [%" PRISIZE_T "] (%.02f,%.02f) (%.02f,%.02f) %p \"%s\"\n",
  205. i, objs->pos.x, objs->pos.y, objs->sz.x, objs->sz.y, objs->lbl,
  206. xp ? ((textlabel_t*)xp->lbl)->text : "");
  207. objs++;
  208. }
  209. fprintf(stderr, "xlabels\n");
  210. for (size_t i = 0; i < n_lbls; i++) {
  211. fprintf(stderr, " [%" PRISIZE_T "] %p set %d (%.02f,%.02f) (%.02f,%.02f) %s\n",
  212. i, lbls, lbls->set, lbls->pos.x, lbls->pos.y, lbls->sz.x,
  213. lbls->sz.y, ((textlabel_t*)lbls->lbl)->text);
  214. lbls++;
  215. }
  216. }
  217. static pointf
  218. edgeTailpoint (Agedge_t* e)
  219. {
  220. splines *spl;
  221. bezier *bez;
  222. if ((spl = getsplinepoints(e)) == NULL) {
  223. pointf p;
  224. p.x = p.y = 0;
  225. return p;
  226. }
  227. bez = &spl->list[0];
  228. if (bez->sflag) {
  229. return bez->sp;
  230. } else {
  231. return bez->list[0];
  232. }
  233. }
  234. static pointf
  235. edgeHeadpoint (Agedge_t* e)
  236. {
  237. splines *spl;
  238. bezier *bez;
  239. if ((spl = getsplinepoints(e)) == NULL) {
  240. pointf p;
  241. p.x = p.y = 0;
  242. return p;
  243. }
  244. bez = &spl->list[spl->size - 1];
  245. if (bez->eflag) {
  246. return bez->ep;
  247. } else {
  248. return bez->list[bez->size - 1];
  249. }
  250. }
  251. /* adjustBB:
  252. */
  253. static boxf
  254. adjustBB (object_t* objp, boxf bb)
  255. {
  256. pointf ur;
  257. /* Adjust bounding box */
  258. bb.LL.x = MIN(bb.LL.x, objp->pos.x);
  259. bb.LL.y = MIN(bb.LL.y, objp->pos.y);
  260. ur.x = objp->pos.x + objp->sz.x;
  261. ur.y = objp->pos.y + objp->sz.y;
  262. bb.UR.x = MAX(bb.UR.x, ur.x);
  263. bb.UR.y = MAX(bb.UR.y, ur.y);
  264. return bb;
  265. }
  266. /* addXLabel:
  267. * Set up xlabel_t object and connect with related object.
  268. * If initObj is set, initialize the object.
  269. */
  270. static void
  271. addXLabel (textlabel_t* lp, object_t* objp, xlabel_t* xlp, int initObj, pointf pos)
  272. {
  273. if (initObj) {
  274. *objp = (object_t){.pos = pos};
  275. }
  276. if (Flip) {
  277. xlp->sz.x = lp->dimen.y;
  278. xlp->sz.y = lp->dimen.x;
  279. }
  280. else {
  281. xlp->sz = lp->dimen;
  282. }
  283. xlp->lbl = lp;
  284. xlp->set = 0;
  285. objp->lbl = xlp;
  286. }
  287. /* addLabelObj:
  288. * Set up obstacle object based on set external label.
  289. * This includes dot edge labels.
  290. * Use label information to determine size and position of object.
  291. * Then adjust given bounding box bb to include label and return new bb.
  292. */
  293. static boxf
  294. addLabelObj (textlabel_t* lp, object_t* objp, boxf bb)
  295. {
  296. if (Flip) {
  297. objp->sz.x = lp->dimen.y;
  298. objp->sz.y = lp->dimen.x;
  299. }
  300. else {
  301. objp->sz.x = lp->dimen.x;
  302. objp->sz.y = lp->dimen.y;
  303. }
  304. objp->pos = lp->pos;
  305. objp->pos.x -= objp->sz.x / 2.0;
  306. objp->pos.y -= objp->sz.y / 2.0;
  307. return adjustBB(objp, bb);
  308. }
  309. /* addNodeOjb:
  310. * Set up obstacle object based on a node.
  311. * Use node information to determine size and position of object.
  312. * Then adjust given bounding box bb to include label and return new bb.
  313. */
  314. static boxf
  315. addNodeObj (node_t* np, object_t* objp, boxf bb)
  316. {
  317. if (Flip) {
  318. objp->sz.x = INCH2PS(ND_height(np));
  319. objp->sz.y = INCH2PS(ND_width(np));
  320. }
  321. else {
  322. objp->sz.x = INCH2PS(ND_width(np));
  323. objp->sz.y = INCH2PS(ND_height(np));
  324. }
  325. objp->pos = ND_coord(np);
  326. objp->pos.x -= objp->sz.x / 2.0;
  327. objp->pos.y -= objp->sz.y / 2.0;
  328. return adjustBB(objp, bb);
  329. }
  330. typedef struct {
  331. boxf bb;
  332. object_t* objp;
  333. } cinfo_t;
  334. static cinfo_t
  335. addClusterObj (Agraph_t* g, cinfo_t info)
  336. {
  337. int c;
  338. for (c = 1; c <= GD_n_cluster(g); c++)
  339. info = addClusterObj (GD_clust(g)[c], info);
  340. if (g != agroot(g) && GD_label(g) && GD_label(g)->set) {
  341. object_t* objp = info.objp;
  342. info.bb = addLabelObj (GD_label(g), objp, info.bb);
  343. info.objp++;
  344. }
  345. return info;
  346. }
  347. static size_t countClusterLabels(Agraph_t *g) {
  348. size_t i = 0;
  349. if (g != agroot(g) && GD_label(g) && GD_label(g)->set)
  350. i++;
  351. for (int c = 1; c <= GD_n_cluster(g); c++)
  352. i += countClusterLabels (GD_clust(g)[c]);
  353. return i;
  354. }
  355. /* True if edges geometries were computed and this edge has a geometry */
  356. #define HAVE_EDGE(ep) ((et != EDGETYPE_NONE) && (ED_spl(ep) != NULL))
  357. /// position xlabels and any unpositioned edge labels using a map placement
  358. /// algorithm to avoid overlap
  359. ///
  360. /// TODO: interaction with spline=ortho
  361. static void addXLabels(Agraph_t * gp)
  362. {
  363. Agnode_t *np;
  364. Agedge_t *ep;
  365. size_t n_nlbls = 0; // # of unset node xlabels
  366. size_t n_elbls = 0; // # of unset edge labels or xlabels
  367. size_t n_set_lbls = 0; // # of set xlabels and edge labels
  368. size_t n_clbls = 0; // # of set cluster labels
  369. boxf bb;
  370. textlabel_t* lp;
  371. label_params_t params;
  372. object_t* objs;
  373. xlabel_t* lbls;
  374. Agsym_t* force;
  375. int et = EDGE_TYPE(gp);
  376. if (!(GD_has_labels(gp) & NODE_XLABEL) &&
  377. !(GD_has_labels(gp) & EDGE_XLABEL) &&
  378. !(GD_has_labels(gp) & TAIL_LABEL) &&
  379. !(GD_has_labels(gp) & HEAD_LABEL) &&
  380. (!(GD_has_labels(gp) & EDGE_LABEL) || EdgeLabelsDone))
  381. return;
  382. for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
  383. if (ND_xlabel(np)) {
  384. if (ND_xlabel(np)->set)
  385. n_set_lbls++;
  386. else
  387. n_nlbls++;
  388. }
  389. for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
  390. if (ED_xlabel(ep)) {
  391. if (ED_xlabel(ep)->set)
  392. n_set_lbls++;
  393. else if (HAVE_EDGE(ep))
  394. n_elbls++;
  395. }
  396. if (ED_head_label(ep)) {
  397. if (ED_head_label(ep)->set)
  398. n_set_lbls++;
  399. else if (HAVE_EDGE(ep))
  400. n_elbls++;
  401. }
  402. if (ED_tail_label(ep)) {
  403. if (ED_tail_label(ep)->set)
  404. n_set_lbls++;
  405. else if (HAVE_EDGE(ep))
  406. n_elbls++;
  407. }
  408. if (ED_label(ep)) {
  409. if (ED_label(ep)->set)
  410. n_set_lbls++;
  411. else if (HAVE_EDGE(ep))
  412. n_elbls++;
  413. }
  414. }
  415. }
  416. if (GD_has_labels(gp) & GRAPH_LABEL)
  417. n_clbls = countClusterLabels (gp);
  418. /* A label for each unpositioned external label */
  419. size_t n_lbls = n_nlbls + n_elbls;
  420. if (n_lbls == 0) return;
  421. /* An object for each node, each positioned external label, any cluster label,
  422. * and all unset edge labels and xlabels.
  423. */
  424. size_t n_objs = (size_t)agnnodes(gp) + n_set_lbls + n_clbls + n_elbls;
  425. object_t* objp = objs = gv_calloc(n_objs, sizeof(object_t));
  426. xlabel_t* xlp = lbls = gv_calloc(n_lbls, sizeof(xlabel_t));
  427. bb.LL = (pointf){INT_MAX, INT_MAX};
  428. bb.UR = (pointf){-INT_MAX, -INT_MAX};
  429. for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
  430. bb = addNodeObj (np, objp, bb);
  431. if ((lp = ND_xlabel(np))) {
  432. if (lp->set) {
  433. objp++;
  434. bb = addLabelObj (lp, objp, bb);
  435. }
  436. else {
  437. pointf ignored = { 0.0, 0.0 };
  438. addXLabel (lp, objp, xlp, 0, ignored);
  439. xlp++;
  440. }
  441. }
  442. objp++;
  443. for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
  444. if ((lp = ED_label(ep))) {
  445. if (lp->set) {
  446. bb = addLabelObj (lp, objp, bb);
  447. }
  448. else if (HAVE_EDGE(ep)) {
  449. addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep));
  450. xlp++;
  451. }
  452. else {
  453. agwarningf("no position for edge with label %s\n",
  454. ED_label(ep)->text);
  455. continue;
  456. }
  457. objp++;
  458. }
  459. if ((lp = ED_tail_label(ep))) {
  460. if (lp->set) {
  461. bb = addLabelObj (lp, objp, bb);
  462. }
  463. else if (HAVE_EDGE(ep)) {
  464. addXLabel (lp, objp, xlp, 1, edgeTailpoint(ep));
  465. xlp++;
  466. }
  467. else {
  468. agwarningf("no position for edge with tail label %s\n",
  469. ED_tail_label(ep)->text);
  470. continue;
  471. }
  472. objp++;
  473. }
  474. if ((lp = ED_head_label(ep))) {
  475. if (lp->set) {
  476. bb = addLabelObj (lp, objp, bb);
  477. }
  478. else if (HAVE_EDGE(ep)) {
  479. addXLabel (lp, objp, xlp, 1, edgeHeadpoint(ep));
  480. xlp++;
  481. }
  482. else {
  483. agwarningf("no position for edge with head label %s\n",
  484. ED_head_label(ep)->text);
  485. continue;
  486. }
  487. objp++;
  488. }
  489. if ((lp = ED_xlabel(ep))) {
  490. if (lp->set) {
  491. bb = addLabelObj (lp, objp, bb);
  492. }
  493. else if (HAVE_EDGE(ep)) {
  494. addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep));
  495. xlp++;
  496. }
  497. else {
  498. agwarningf("no position for edge with xlabel %s\n",
  499. ED_xlabel(ep)->text);
  500. continue;
  501. }
  502. objp++;
  503. }
  504. }
  505. }
  506. if (n_clbls) {
  507. cinfo_t info;
  508. info.bb = bb;
  509. info.objp = objp;
  510. info = addClusterObj (gp, info);
  511. bb = info.bb;
  512. }
  513. force = agfindgraphattr(gp, "forcelabels");
  514. params.force = late_bool(gp, force, true);
  515. params.bb = bb;
  516. placeLabels(objs, n_objs, lbls, n_lbls, &params);
  517. if (Verbose)
  518. printData(objs, n_objs, lbls, n_lbls, &params);
  519. xlp = lbls;
  520. size_t cnt = 0;
  521. for (size_t i = 0; i < n_lbls; i++) {
  522. if (xlp->set) {
  523. cnt++;
  524. lp = (textlabel_t *)xlp->lbl;
  525. lp->set = 1;
  526. lp->pos = centerPt(xlp);
  527. updateBB (gp, lp);
  528. }
  529. xlp++;
  530. }
  531. if (Verbose)
  532. fprintf(stderr, "%" PRISIZE_T " out of %" PRISIZE_T " labels positioned.\n",
  533. cnt, n_lbls);
  534. else if (cnt != n_lbls)
  535. agwarningf("%" PRISIZE_T " out of %" PRISIZE_T " exterior labels positioned.\n",
  536. cnt, n_lbls);
  537. free(objs);
  538. free(lbls);
  539. }
  540. /* gv_postprocess:
  541. * Set graph and cluster label positions.
  542. * Add space for root graph label and translate graph accordingly.
  543. * Set final nodesize using ns.
  544. * Assumes the boxes of all clusters have been computed.
  545. * When done, the bounding box of g has LL at origin.
  546. */
  547. void gv_postprocess(Agraph_t * g, int allowTranslation)
  548. {
  549. double diff;
  550. pointf dimen = { 0., 0. };
  551. Rankdir = GD_rankdir(g);
  552. Flip = GD_flip(g);
  553. /* Handle cluster labels */
  554. if (Flip)
  555. place_flip_graph_label(g);
  556. else
  557. place_graph_label(g);
  558. /* Everything has been placed except the root graph label, if any.
  559. * The graph positions have not yet been rotated back if necessary.
  560. */
  561. addXLabels(g);
  562. /* Add space for graph label if necessary */
  563. if (GD_label(g) && !GD_label(g)->set) {
  564. dimen = GD_label(g)->dimen;
  565. PAD(dimen);
  566. if (Flip) {
  567. if (GD_label_pos(g) & LABEL_AT_TOP) {
  568. GD_bb(g).UR.x += dimen.y;
  569. } else {
  570. GD_bb(g).LL.x -= dimen.y;
  571. }
  572. if (dimen.x > GD_bb(g).UR.y - GD_bb(g).LL.y) {
  573. diff = dimen.x - (GD_bb(g).UR.y - GD_bb(g).LL.y);
  574. diff = diff / 2.;
  575. GD_bb(g).LL.y -= diff;
  576. GD_bb(g).UR.y += diff;
  577. }
  578. } else {
  579. if (GD_label_pos(g) & LABEL_AT_TOP) {
  580. if (Rankdir == RANKDIR_TB)
  581. GD_bb(g).UR.y += dimen.y;
  582. else
  583. GD_bb(g).LL.y -= dimen.y;
  584. } else {
  585. if (Rankdir == RANKDIR_TB)
  586. GD_bb(g).LL.y -= dimen.y;
  587. else
  588. GD_bb(g).UR.y += dimen.y;
  589. }
  590. if (dimen.x > GD_bb(g).UR.x - GD_bb(g).LL.x) {
  591. diff = dimen.x - (GD_bb(g).UR.x - GD_bb(g).LL.x);
  592. diff = diff / 2.;
  593. GD_bb(g).LL.x -= diff;
  594. GD_bb(g).UR.x += diff;
  595. }
  596. }
  597. }
  598. if (allowTranslation) {
  599. switch (Rankdir) {
  600. case RANKDIR_TB:
  601. Offset = GD_bb(g).LL;
  602. break;
  603. case RANKDIR_LR:
  604. Offset = (pointf){-GD_bb(g).UR.y, GD_bb(g).LL.x};
  605. break;
  606. case RANKDIR_BT:
  607. Offset = (pointf){GD_bb(g).LL.x, -GD_bb(g).UR.y};
  608. break;
  609. case RANKDIR_RL:
  610. Offset = (pointf){GD_bb(g).LL.y, GD_bb(g).LL.x};
  611. break;
  612. default:
  613. UNREACHABLE();
  614. }
  615. translate_drawing(g);
  616. }
  617. if (GD_label(g) && !GD_label(g)->set)
  618. place_root_label(g, dimen);
  619. if (!show_boxes_is_empty(&Show_boxes)) {
  620. agxbuf buf = {0};
  621. if (Flip)
  622. agxbprint(&buf, M2, Offset.x, Offset.y, Offset.x, Offset.y);
  623. else
  624. agxbprint(&buf, M1, Offset.y, Offset.x, Offset.y, Offset.x,
  625. -Offset.x, -Offset.y);
  626. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  627. }
  628. }
  629. /* dotneato_postprocess:
  630. */
  631. void dotneato_postprocess(Agraph_t * g)
  632. {
  633. gv_postprocess(g, 1);
  634. }
  635. /* place_flip_graph_label:
  636. * Put cluster labels recursively in the flip case.
  637. */
  638. static void place_flip_graph_label(graph_t * g)
  639. {
  640. int c;
  641. pointf p, d;
  642. if (g != agroot(g) && GD_label(g) && !GD_label(g)->set) {
  643. if (GD_label_pos(g) & LABEL_AT_TOP) {
  644. d = GD_border(g)[RIGHT_IX];
  645. p.x = GD_bb(g).UR.x - d.x / 2;
  646. } else {
  647. d = GD_border(g)[LEFT_IX];
  648. p.x = GD_bb(g).LL.x + d.x / 2;
  649. }
  650. if (GD_label_pos(g) & LABEL_AT_RIGHT) {
  651. p.y = GD_bb(g).LL.y + d.y / 2;
  652. } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
  653. p.y = GD_bb(g).UR.y - d.y / 2;
  654. } else {
  655. p.y = (GD_bb(g).LL.y + GD_bb(g).UR.y) / 2;
  656. }
  657. GD_label(g)->pos = p;
  658. GD_label(g)->set = true;
  659. }
  660. for (c = 1; c <= GD_n_cluster(g); c++)
  661. place_flip_graph_label(GD_clust(g)[c]);
  662. }
  663. /* place_graph_label:
  664. * Put cluster labels recursively in the non-flip case.
  665. * The adjustments to the bounding boxes should no longer
  666. * be necessary, since we now guarantee the label fits in
  667. * the cluster.
  668. */
  669. void place_graph_label(graph_t * g)
  670. {
  671. int c;
  672. pointf p, d;
  673. if (g != agroot(g) && GD_label(g) && !GD_label(g)->set) {
  674. if (GD_label_pos(g) & LABEL_AT_TOP) {
  675. d = GD_border(g)[TOP_IX];
  676. p.y = GD_bb(g).UR.y - d.y / 2;
  677. } else {
  678. d = GD_border(g)[BOTTOM_IX];
  679. p.y = GD_bb(g).LL.y + d.y / 2;
  680. }
  681. if (GD_label_pos(g) & LABEL_AT_RIGHT) {
  682. p.x = GD_bb(g).UR.x - d.x / 2;
  683. } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
  684. p.x = GD_bb(g).LL.x + d.x / 2;
  685. } else {
  686. p.x = (GD_bb(g).LL.x + GD_bb(g).UR.x) / 2;
  687. }
  688. GD_label(g)->pos = p;
  689. GD_label(g)->set = true;
  690. }
  691. for (c = 1; c <= GD_n_cluster(g); c++)
  692. place_graph_label(GD_clust(g)[c]);
  693. }