routespl.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  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 "config.h"
  13. #include <assert.h>
  14. #include <cgraph/gv_math.h>
  15. #include <cgraph/list.h>
  16. #include <common/geomprocs.h>
  17. #include <common/render.h>
  18. #include <float.h>
  19. #include <limits.h>
  20. #include <math.h>
  21. #include <pathplan/pathplan.h>
  22. #include <stdbool.h>
  23. #include <stdint.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <util/agxbuf.h>
  27. #include <util/alloc.h>
  28. #include <util/prisize_t.h>
  29. static int nedges; ///< total no. of edges used in routing
  30. static size_t nboxes; ///< total no. of boxes used in routing
  31. static int routeinit;
  32. static int checkpath(size_t, boxf *, path *);
  33. static void printpath(path * pp);
  34. #ifdef DEBUG
  35. static void printboxes(size_t boxn, boxf *boxes) {
  36. pointf ll, ur;
  37. for (size_t bi = 0; bi < boxn; bi++) {
  38. ll = boxes[bi].LL, ur = boxes[bi].UR;
  39. agxbuf buf = {0};
  40. agxbprint(&buf, "%.0f %.0f %.0f %.0f pathbox", ll.x, ll.y, ur.x, ur.y);
  41. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  42. }
  43. }
  44. #if DEBUG > 1
  45. static void psprintpolypts(Ppoint_t * p, int sz)
  46. {
  47. int i;
  48. fprintf(stderr, "%%!\n");
  49. fprintf(stderr, "%% constraint poly\n");
  50. fprintf(stderr, "newpath\n");
  51. for (i = 0; i < sz; i++)
  52. fprintf(stderr, "%f %f %s\n", p[i].x, p[i].y, i == 0 ? "moveto" : "lineto");
  53. fprintf(stderr, "closepath stroke\n");
  54. }
  55. static void psprintpoint(point p)
  56. {
  57. fprintf(stderr, "gsave\n");
  58. fprintf(stderr,
  59. "newpath %d %d moveto %d %d 2 0 360 arc closepath fill stroke\n",
  60. p.x, p.y, p.x, p.y);
  61. fprintf(stderr, "/Times-Roman findfont 4 scalefont setfont\n");
  62. fprintf(stderr, "%d %d moveto (\\(%d,%d\\)) show\n", p.x + 5, p.y + 5,
  63. p.x, p.y);
  64. fprintf(stderr, "grestore\n");
  65. }
  66. static void psprintpointf(pointf p)
  67. {
  68. fprintf(stderr, "gsave\n");
  69. fprintf(stderr,
  70. "newpath %.5g %.5g moveto %.5g %.5g 2 0 360 arc closepath fill stroke\n",
  71. p.x, p.y, p.x, p.y);
  72. fprintf(stderr, "/Times-Roman findfont 4 scalefont setfont\n");
  73. fprintf(stderr, "%.5g %.5g moveto (\\(%.5g,%.5g\\)) show\n", p.x + 5, p.y + 5,
  74. p.x, p.y);
  75. fprintf(stderr, "grestore\n");
  76. }
  77. #endif
  78. static void psprintspline(Ppolyline_t spl)
  79. {
  80. show_boxes_append(&Show_boxes, gv_strdup("%%!"));
  81. show_boxes_append(&Show_boxes, gv_strdup("%% spline"));
  82. show_boxes_append(&Show_boxes, gv_strdup("gsave 1 0 0 setrgbcolor newpath"));
  83. for (size_t i = 0; i < spl.pn; i++) {
  84. agxbuf buf = {0};
  85. agxbprint(&buf, "%f %f %s", spl.ps[i].x, spl.ps[i].y,
  86. i == 0 ? "moveto" : (i % 3 == 0 ? "curveto" : ""));
  87. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  88. }
  89. show_boxes_append(&Show_boxes, gv_strdup("stroke grestore"));
  90. }
  91. static void psprintline(Ppolyline_t pl)
  92. {
  93. show_boxes_append(&Show_boxes, gv_strdup("%%!"));
  94. show_boxes_append(&Show_boxes, gv_strdup("%% line"));
  95. show_boxes_append(&Show_boxes, gv_strdup("gsave 0 0 1 setrgbcolor newpath"));
  96. for (size_t i = 0; i < pl.pn; i++) {
  97. agxbuf buf = {0};
  98. agxbprint(&buf, "%f %f %s", pl.ps[i].x, pl.ps[i].y,
  99. i == 0 ? "moveto" : "lineto");
  100. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  101. }
  102. show_boxes_append(&Show_boxes, gv_strdup("stroke grestore"));
  103. }
  104. static void psprintpoly(Ppoly_t p)
  105. {
  106. char* pfx;
  107. show_boxes_append(&Show_boxes, gv_strdup("%% poly list"));
  108. show_boxes_append(&Show_boxes, gv_strdup("gsave 0 1 0 setrgbcolor"));
  109. for (size_t bi = 0; bi < p.pn; bi++) {
  110. const pointf tail = p.ps[bi];
  111. const pointf head = p.ps[(bi + 1) % p.pn];
  112. if (fabs(tail.x - head.x) < 1 && fabs(tail.y - head.y) < 1) pfx = "%%";
  113. else pfx ="";
  114. agxbuf buf = {0};
  115. agxbprint(&buf, "%s%.0f %.0f %.0f %.0f makevec", pfx, tail.x, tail.y, head.x,
  116. head.y);
  117. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  118. }
  119. show_boxes_append(&Show_boxes, gv_strdup("grestore"));
  120. }
  121. static void psprintboxes(size_t boxn, boxf *boxes) {
  122. pointf ll, ur;
  123. show_boxes_append(&Show_boxes, gv_strdup("%% box list"));
  124. show_boxes_append(&Show_boxes, gv_strdup("gsave 0 1 0 setrgbcolor"));
  125. for (size_t bi = 0; bi < boxn; bi++) {
  126. ll = boxes[bi].LL, ur = boxes[bi].UR;
  127. agxbuf buf = {0};
  128. agxbprint(&buf, "newpath\n%.0f %.0f moveto", ll.x, ll.y);
  129. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  130. agxbprint(&buf, "%.0f %.0f lineto", ll.x, ur.y);
  131. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  132. agxbprint(&buf, "%.0f %.0f lineto", ur.x, ur.y);
  133. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  134. agxbprint(&buf, "%.0f %.0f lineto", ur.x, ll.y);
  135. show_boxes_append(&Show_boxes, agxbdisown(&buf));
  136. show_boxes_append(&Show_boxes, gv_strdup("closepath stroke"));
  137. }
  138. show_boxes_append(&Show_boxes, gv_strdup("grestore"));
  139. }
  140. static void psprintinit (int begin)
  141. {
  142. if (begin)
  143. show_boxes_append(&Show_boxes, gv_strdup("dbgstart"));
  144. else
  145. show_boxes_append(&Show_boxes, gv_strdup("grestore"));
  146. }
  147. static bool debugleveln(edge_t* realedge, int i)
  148. {
  149. return GD_showboxes(agraphof(aghead(realedge))) == i ||
  150. GD_showboxes(agraphof(agtail(realedge))) == i ||
  151. ED_showboxes(realedge) == i ||
  152. ND_showboxes(aghead(realedge)) == i ||
  153. ND_showboxes(agtail(realedge)) == i;
  154. }
  155. #endif /* DEBUG */
  156. /// Given a simple (ccw) polygon, route an edge from tp to hp.
  157. pointf *simpleSplineRoute(pointf tp, pointf hp, Ppoly_t poly, size_t *n_spl_pts,
  158. int polyline) {
  159. Ppolyline_t pl, spl;
  160. Ppoint_t eps[2];
  161. Pvector_t evs[2];
  162. eps[0].x = tp.x;
  163. eps[0].y = tp.y;
  164. eps[1].x = hp.x;
  165. eps[1].y = hp.y;
  166. if (Pshortestpath(&poly, eps, &pl) < 0)
  167. return NULL;
  168. if (polyline)
  169. make_polyline (pl, &spl);
  170. else {
  171. // polygon edges passed to Proutespline
  172. Pedge_t *edges = gv_calloc(poly.pn, sizeof(Pedge_t));
  173. for (size_t i = 0; i < poly.pn; i++) {
  174. edges[i].a = poly.ps[i];
  175. edges[i].b = poly.ps[(i + 1) % poly.pn];
  176. }
  177. evs[0].x = evs[0].y = 0;
  178. evs[1].x = evs[1].y = 0;
  179. if (Proutespline(edges, poly.pn, pl, evs, &spl) < 0) {
  180. free(edges);
  181. return NULL;
  182. }
  183. free(edges);
  184. }
  185. pointf *ps = calloc(spl.pn, sizeof(ps[0]));
  186. if (ps == NULL) {
  187. agerrorf("cannot allocate ps\n");
  188. return NULL;
  189. }
  190. for (size_t i = 0; i < spl.pn; i++) {
  191. ps[i] = spl.ps[i];
  192. }
  193. *n_spl_pts = spl.pn;
  194. return ps;
  195. }
  196. /** Data initialized once until matching call to routeplineterm
  197. * Allows recursive calls to dot
  198. */
  199. int
  200. routesplinesinit(void)
  201. {
  202. if (++routeinit > 1) return 0;
  203. #ifdef DEBUG
  204. show_boxes_free(&Show_boxes);
  205. #endif
  206. nedges = 0;
  207. nboxes = 0;
  208. if (Verbose)
  209. start_timer();
  210. return 0;
  211. }
  212. void routesplinesterm(void)
  213. {
  214. if (--routeinit > 0) return;
  215. if (Verbose)
  216. fprintf(stderr,
  217. "routesplines: %d edges, %" PRISIZE_T " boxes %.2f sec\n",
  218. nedges, nboxes, elapsed_sec());
  219. }
  220. static void limitBoxes(boxf *boxes, size_t boxn, const pointf *pps, size_t pn,
  221. double delta) {
  222. double t;
  223. pointf sp[4];
  224. const double num_div = delta * (double)boxn;
  225. for (size_t splinepi = 0; splinepi + 3 < pn; splinepi += 3) {
  226. for (double si = 0; si <= num_div; si++) {
  227. t = si / num_div;
  228. sp[0] = pps[splinepi];
  229. sp[1] = pps[splinepi + 1];
  230. sp[2] = pps[splinepi + 2];
  231. sp[3] = pps[splinepi + 3];
  232. sp[0].x += t * (sp[1].x - sp[0].x);
  233. sp[0].y += t * (sp[1].y - sp[0].y);
  234. sp[1].x += t * (sp[2].x - sp[1].x);
  235. sp[1].y += t * (sp[2].y - sp[1].y);
  236. sp[2].x += t * (sp[3].x - sp[2].x);
  237. sp[2].y += t * (sp[3].y - sp[2].y);
  238. sp[0].x += t * (sp[1].x - sp[0].x);
  239. sp[0].y += t * (sp[1].y - sp[0].y);
  240. sp[1].x += t * (sp[2].x - sp[1].x);
  241. sp[1].y += t * (sp[2].y - sp[1].y);
  242. sp[0].x += t * (sp[1].x - sp[0].x);
  243. sp[0].y += t * (sp[1].y - sp[0].y);
  244. for (size_t bi = 0; bi < boxn; bi++) {
  245. /* this tested ok on 64bit machines, but on 32bit we need this FUDGE
  246. * or graphs/directed/records.gv fails */
  247. #define FUDGE .0001
  248. if (sp[0].y <= boxes[bi].UR.y+FUDGE && sp[0].y >= boxes[bi].LL.y-FUDGE) {
  249. boxes[bi].LL.x = fmin(boxes[bi].LL.x, sp[0].x);
  250. boxes[bi].UR.x = fmax(boxes[bi].UR.x, sp[0].x);
  251. }
  252. }
  253. }
  254. }
  255. }
  256. #define INIT_DELTA 10
  257. #define LOOP_TRIES 15 /* number of times to try to limiting boxes to regain space, using smaller divisions */
  258. /** Route a path using the path info in pp. This includes start and end points
  259. * plus a collection of contiguous boxes contain the terminal points. The boxes
  260. * are converted into a containing polygon. A shortest path is constructed within
  261. * the polygon from between the terminal points. If polyline is true, this path
  262. * is converted to a spline representation. Otherwise, we call the path planner to
  263. * convert the polyline into a smooth spline staying within the polygon. In both
  264. * cases, the function returns an array of the computed control points. The number
  265. * of these points is given in npoints.
  266. *
  267. * Note that the returned points are stored in a single array, so the points must be
  268. * used before another call to this function.
  269. *
  270. * During cleanup, the function determines the x-extent of the spline in the box, so
  271. * the box can be shrunk to the minimum width. The extra space can then be used by other
  272. * edges.
  273. *
  274. * If a catastrophic error, return NULL and npoints is 0.
  275. */
  276. static pointf *routesplines_(path *pp, size_t *npoints, int polyline) {
  277. Ppoly_t poly;
  278. Ppolyline_t pl, spl;
  279. Ppoint_t eps[2];
  280. Pvector_t evs[2];
  281. int prev, next;
  282. boxf *boxes;
  283. edge_t* realedge;
  284. bool flip;
  285. int loopcnt;
  286. bool unbounded;
  287. *npoints = 0;
  288. nedges++;
  289. nboxes += pp->nbox;
  290. for (realedge = pp->data;
  291. realedge && ED_edge_type(realedge) != NORMAL;
  292. realedge = ED_to_orig(realedge));
  293. if (!realedge) {
  294. agerrorf("in routesplines, cannot find NORMAL edge\n");
  295. return NULL;
  296. }
  297. boxes = pp->boxes;
  298. const size_t boxn = pp->nbox;
  299. if (checkpath(boxn, boxes, pp))
  300. return NULL;
  301. #ifdef DEBUG
  302. if (debugleveln(realedge, 1))
  303. printboxes(boxn, boxes);
  304. if (debugleveln(realedge, 3)) {
  305. psprintinit(1);
  306. psprintboxes(boxn, boxes);
  307. }
  308. #endif
  309. // vertices of polygon defined by boxes
  310. Ppoint_t *polypoints = gv_calloc(boxn * 8, sizeof(Ppoint_t));
  311. if (boxn > 1 && boxes[0].LL.y > boxes[1].LL.y) {
  312. flip = true;
  313. for (size_t bi = 0; bi < boxn; bi++) {
  314. double v = boxes[bi].UR.y;
  315. boxes[bi].UR.y = -1*boxes[bi].LL.y;
  316. boxes[bi].LL.y = -v;
  317. }
  318. }
  319. else flip = false;
  320. size_t pi;
  321. if (agtail(realedge) != aghead(realedge)) {
  322. /* I assume that the path goes either down only or
  323. up - right - down */
  324. size_t bi;
  325. for (bi = 0, pi = 0; bi < boxn; bi++) {
  326. next = prev = 0;
  327. if (bi > 0)
  328. prev = boxes[bi].LL.y > boxes[bi - 1].LL.y ? -1 : 1;
  329. if (bi + 1 < boxn)
  330. next = boxes[bi + 1].LL.y > boxes[bi].LL.y ? 1 : -1;
  331. if (prev != next) {
  332. if (next == -1 || prev == 1) {
  333. polypoints[pi].x = boxes[bi].LL.x;
  334. polypoints[pi++].y = boxes[bi].UR.y;
  335. polypoints[pi].x = boxes[bi].LL.x;
  336. polypoints[pi++].y = boxes[bi].LL.y;
  337. } else {
  338. polypoints[pi].x = boxes[bi].UR.x;
  339. polypoints[pi++].y = boxes[bi].LL.y;
  340. polypoints[pi].x = boxes[bi].UR.x;
  341. polypoints[pi++].y = boxes[bi].UR.y;
  342. }
  343. }
  344. else if (prev == 0) { /* single box */
  345. polypoints[pi].x = boxes[bi].LL.x;
  346. polypoints[pi++].y = boxes[bi].UR.y;
  347. polypoints[pi].x = boxes[bi].LL.x;
  348. polypoints[pi++].y = boxes[bi].LL.y;
  349. }
  350. else {
  351. if (!(prev == -1 && next == -1)) {
  352. free(polypoints);
  353. agerrorf("in routesplines, illegal values of prev %d and next %d, line %d\n", prev, next, __LINE__);
  354. return NULL;
  355. }
  356. }
  357. }
  358. for (bi = boxn - 1; bi != SIZE_MAX; bi--) {
  359. next = prev = 0;
  360. if (bi + 1 < boxn)
  361. prev = boxes[bi].LL.y > boxes[bi + 1].LL.y ? -1 : 1;
  362. if (bi > 0)
  363. next = boxes[bi - 1].LL.y > boxes[bi].LL.y ? 1 : -1;
  364. if (prev != next) {
  365. if (next == -1 || prev == 1 ) {
  366. polypoints[pi].x = boxes[bi].LL.x;
  367. polypoints[pi++].y = boxes[bi].UR.y;
  368. polypoints[pi].x = boxes[bi].LL.x;
  369. polypoints[pi++].y = boxes[bi].LL.y;
  370. } else {
  371. polypoints[pi].x = boxes[bi].UR.x;
  372. polypoints[pi++].y = boxes[bi].LL.y;
  373. polypoints[pi].x = boxes[bi].UR.x;
  374. polypoints[pi++].y = boxes[bi].UR.y;
  375. }
  376. }
  377. else if (prev == 0) { /* single box */
  378. polypoints[pi].x = boxes[bi].UR.x;
  379. polypoints[pi++].y = boxes[bi].LL.y;
  380. polypoints[pi].x = boxes[bi].UR.x;
  381. polypoints[pi++].y = boxes[bi].UR.y;
  382. }
  383. else {
  384. if (!(prev == -1 && next == -1)) {
  385. /* it went badly, e.g. degenerate box in boxlist */
  386. free(polypoints);
  387. agerrorf("in routesplines, illegal values of prev %d and next %d, line %d\n", prev, next, __LINE__);
  388. return NULL; /* for correctness sake, it's best to just stop */
  389. }
  390. polypoints[pi].x = boxes[bi].UR.x;
  391. polypoints[pi++].y = boxes[bi].LL.y;
  392. polypoints[pi].x = boxes[bi].UR.x;
  393. polypoints[pi++].y = boxes[bi].UR.y;
  394. polypoints[pi].x = boxes[bi].LL.x;
  395. polypoints[pi++].y = boxes[bi].UR.y;
  396. polypoints[pi].x = boxes[bi].LL.x;
  397. polypoints[pi++].y = boxes[bi].LL.y;
  398. }
  399. }
  400. }
  401. else {
  402. free(polypoints);
  403. agerrorf("in routesplines, edge is a loop at %s\n", agnameof(aghead(realedge)));
  404. return NULL;
  405. }
  406. if (flip) {
  407. for (size_t bi = 0; bi < boxn; bi++) {
  408. double v = boxes[bi].UR.y;
  409. boxes[bi].UR.y = -1*boxes[bi].LL.y;
  410. boxes[bi].LL.y = -v;
  411. }
  412. for (size_t i = 0; i < pi; i++)
  413. polypoints[i].y *= -1;
  414. }
  415. static const double INITIAL_LLX = DBL_MAX;
  416. static const double INITIAL_URX = -DBL_MAX;
  417. for (size_t bi = 0; bi < boxn; bi++) {
  418. boxes[bi].LL.x = INITIAL_LLX;
  419. boxes[bi].UR.x = INITIAL_URX;
  420. }
  421. poly.ps = polypoints, poly.pn = pi;
  422. eps[0].x = pp->start.p.x, eps[0].y = pp->start.p.y;
  423. eps[1].x = pp->end.p.x, eps[1].y = pp->end.p.y;
  424. if (Pshortestpath(&poly, eps, &pl) < 0) {
  425. free(polypoints);
  426. agerrorf("in routesplines, Pshortestpath failed\n");
  427. return NULL;
  428. }
  429. #ifdef DEBUG
  430. if (debugleveln(realedge, 3)) {
  431. psprintpoly(poly);
  432. psprintline(pl);
  433. }
  434. #endif
  435. if (polyline) {
  436. make_polyline (pl, &spl);
  437. }
  438. else {
  439. Pedge_t *edges = gv_calloc(poly.pn, sizeof(Pedge_t));
  440. for (size_t edgei = 0; edgei < poly.pn; edgei++) {
  441. edges[edgei].a = polypoints[edgei];
  442. edges[edgei].b = polypoints[(edgei + 1) % poly.pn];
  443. }
  444. if (pp->start.constrained) {
  445. evs[0].x = cos(pp->start.theta);
  446. evs[0].y = sin(pp->start.theta);
  447. } else
  448. evs[0].x = evs[0].y = 0;
  449. if (pp->end.constrained) {
  450. evs[1].x = -cos(pp->end.theta);
  451. evs[1].y = -sin(pp->end.theta);
  452. } else
  453. evs[1].x = evs[1].y = 0;
  454. if (Proutespline(edges, poly.pn, pl, evs, &spl) < 0) {
  455. free(edges);
  456. free(polypoints);
  457. agerrorf("in routesplines, Proutespline failed\n");
  458. return NULL;
  459. }
  460. free(edges);
  461. #ifdef DEBUG
  462. if (debugleveln(realedge, 3)) {
  463. psprintspline(spl);
  464. psprintinit(0);
  465. }
  466. #endif
  467. }
  468. pointf *ps = calloc(spl.pn, sizeof(ps[0]));
  469. if (ps == NULL) {
  470. free(polypoints);
  471. agerrorf("cannot allocate ps\n");
  472. return NULL; /* Bailout if no memory left */
  473. }
  474. unbounded = true;
  475. for (size_t splinepi = 0; splinepi < spl.pn; splinepi++) {
  476. ps[splinepi] = spl.ps[splinepi];
  477. }
  478. double delta = INIT_DELTA;
  479. for (loopcnt = 0; unbounded && loopcnt < LOOP_TRIES; loopcnt++) {
  480. limitBoxes(boxes, boxn, ps, spl.pn, delta);
  481. /* The following check is necessary because if a box is not very
  482. * high, it is possible that the sampling above might miss it.
  483. * Therefore, we make the sample finer until all boxes have
  484. * valid values. cf. bug 456.
  485. */
  486. size_t bi;
  487. for (bi = 0; bi < boxn; bi++) {
  488. /* these fp equality tests are used only to detect if the
  489. * values have been changed since initialization - ok */
  490. if (is_exactly_equal(boxes[bi].LL.x, INITIAL_LLX) ||
  491. is_exactly_equal(boxes[bi].UR.x, INITIAL_URX)) {
  492. delta *= 2; /* try again with a finer interval */
  493. break;
  494. }
  495. }
  496. if (bi == boxn)
  497. unbounded = false;
  498. }
  499. if (unbounded) {
  500. /* Either an extremely short, even degenerate, box, or some failure with the path
  501. * planner causing the spline to miss some boxes. In any case, use the shortest path
  502. * to bound the boxes. This will probably mean a bad edge, but we avoid an infinite
  503. * loop and we can see the bad edge, and even use the showboxes scaffolding.
  504. */
  505. Ppolyline_t polyspl;
  506. agwarningf("Unable to reclaim box space in spline routing for edge \"%s\" -> \"%s\". Something is probably seriously wrong.\n", agnameof(agtail(realedge)), agnameof(aghead(realedge)));
  507. make_polyline (pl, &polyspl);
  508. limitBoxes(boxes, boxn, polyspl.ps, polyspl.pn, INIT_DELTA);
  509. }
  510. *npoints = spl.pn;
  511. #ifdef DEBUG
  512. if (GD_showboxes(agraphof(aghead(realedge))) == 2 ||
  513. GD_showboxes(agraphof(agtail(realedge))) == 2 ||
  514. ED_showboxes(realedge) == 2 ||
  515. ND_showboxes(aghead(realedge)) == 2 ||
  516. ND_showboxes(agtail(realedge)) == 2)
  517. printboxes(boxn, boxes);
  518. #endif
  519. free(polypoints);
  520. return ps;
  521. }
  522. pointf *routesplines(path *pp, size_t *npoints) {
  523. return routesplines_(pp, npoints, 0);
  524. }
  525. pointf *routepolylines(path *pp, size_t *npoints) {
  526. return routesplines_(pp, npoints, 1);
  527. }
  528. static double overlap(double i0, double i1, double j0, double j1) {
  529. if (i1 <= j0)
  530. return 0;
  531. if (i0 >= j1)
  532. return 0;
  533. // does the first interval subsume the second?
  534. if (i0 <= j0 && i1 >= j1)
  535. return i1 - i0;
  536. // does the second interval subsume the first?
  537. if (j0 <= i0 && j1 >= i1)
  538. return j1 - j0;
  539. if (j0 <= i0 && i0 <= j1)
  540. return j1 - i0;
  541. assert(j0 <= i1 && i1 <= j1);
  542. return i1 - j0;
  543. }
  544. /*
  545. * repairs minor errors in the boxpath, such as boxes not joining
  546. * or slightly intersecting. it's sort of the equivalent of the
  547. * audit process in the 5E control program - if you've given up on
  548. * fixing all the bugs, at least try to engineer around them!
  549. * in postmodern CS, we could call this "self-healing code."
  550. *
  551. * Return 1 on failure; 0 on success.
  552. */
  553. static int checkpath(size_t boxn, boxf *boxes, path *thepath) {
  554. boxf *ba, *bb;
  555. int errs, l, r, d, u;
  556. /* remove degenerate boxes. */
  557. size_t i = 0;
  558. for (size_t bi = 0; bi < boxn; bi++) {
  559. if (fabs(boxes[bi].LL.y - boxes[bi].UR.y) < .01)
  560. continue;
  561. if (fabs(boxes[bi].LL.x - boxes[bi].UR.x) < .01)
  562. continue;
  563. boxes[i] = boxes[bi];
  564. i++;
  565. }
  566. boxn = i;
  567. ba = &boxes[0];
  568. if (ba->LL.x > ba->UR.x || ba->LL.y > ba->UR.y) {
  569. agerrorf("in checkpath, box 0 has LL coord > UR coord\n");
  570. printpath(thepath);
  571. return 1;
  572. }
  573. for (size_t bi = 0; bi + 1 < boxn; bi++) {
  574. ba = &boxes[bi], bb = &boxes[bi + 1];
  575. if (bb->LL.x > bb->UR.x || bb->LL.y > bb->UR.y) {
  576. agerrorf("in checkpath, box %" PRISIZE_T " has LL coord > UR coord\n",
  577. bi + 1);
  578. printpath(thepath);
  579. return 1;
  580. }
  581. l = ba->UR.x < bb->LL.x ? 1 : 0;
  582. r = ba->LL.x > bb->UR.x ? 1 : 0;
  583. d = ba->UR.y < bb->LL.y ? 1 : 0;
  584. u = ba->LL.y > bb->UR.y ? 1 : 0;
  585. errs = l + r + d + u;
  586. if (errs > 0 && Verbose) {
  587. fprintf(stderr, "in checkpath, boxes %" PRISIZE_T " and %" PRISIZE_T
  588. " don't touch\n", bi, bi + 1);
  589. printpath(thepath);
  590. }
  591. if (errs > 0) {
  592. double xy;
  593. if (l == 1)
  594. xy = ba->UR.x, ba->UR.x = bb->LL.x, bb->LL.x = xy, l = 0;
  595. else if (r == 1)
  596. xy = ba->LL.x, ba->LL.x = bb->UR.x, bb->UR.x = xy, r = 0;
  597. else if (d == 1)
  598. xy = ba->UR.y, ba->UR.y = bb->LL.y, bb->LL.y = xy, d = 0;
  599. else if (u == 1)
  600. xy = ba->LL.y, ba->LL.y = bb->UR.y, bb->UR.y = xy, u = 0;
  601. for (int j = 0; j < errs - 1; j++) {
  602. if (l == 1)
  603. xy = (ba->UR.x + bb->LL.x) / 2.0 + 0.5, ba->UR.x =
  604. bb->LL.x = xy, l = 0;
  605. else if (r == 1)
  606. xy = (ba->LL.x + bb->UR.x) / 2.0 + 0.5, ba->LL.x =
  607. bb->UR.x = xy, r = 0;
  608. else if (d == 1)
  609. xy = (ba->UR.y + bb->LL.y) / 2.0 + 0.5, ba->UR.y =
  610. bb->LL.y = xy, d = 0;
  611. else if (u == 1)
  612. xy = (ba->LL.y + bb->UR.y) / 2.0 + 0.5, ba->LL.y =
  613. bb->UR.y = xy, u = 0;
  614. }
  615. }
  616. /* check for overlapping boxes */
  617. double xoverlap = overlap(ba->LL.x, ba->UR.x, bb->LL.x, bb->UR.x);
  618. double yoverlap = overlap(ba->LL.y, ba->UR.y, bb->LL.y, bb->UR.y);
  619. if (xoverlap > 0 && yoverlap > 0) {
  620. if (xoverlap < yoverlap) {
  621. if (ba->UR.x - ba->LL.x > bb->UR.x - bb->LL.x) {
  622. /* take space from ba */
  623. if (ba->UR.x < bb->UR.x)
  624. ba->UR.x = bb->LL.x;
  625. else
  626. ba->LL.x = bb->UR.x;
  627. } else {
  628. /* take space from bb */
  629. if (ba->UR.x < bb->UR.x)
  630. bb->LL.x = ba->UR.x;
  631. else
  632. bb->UR.x = ba->LL.x;
  633. }
  634. } else { /* symmetric for y coords */
  635. if (ba->UR.y - ba->LL.y > bb->UR.y - bb->LL.y) {
  636. /* take space from ba */
  637. if (ba->UR.y < bb->UR.y)
  638. ba->UR.y = bb->LL.y;
  639. else
  640. ba->LL.y = bb->UR.y;
  641. } else {
  642. /* take space from bb */
  643. if (ba->UR.y < bb->UR.y)
  644. bb->LL.y = ba->UR.y;
  645. else
  646. bb->UR.y = ba->LL.y;
  647. }
  648. }
  649. }
  650. }
  651. if (thepath->start.p.x < boxes[0].LL.x
  652. || thepath->start.p.x > boxes[0].UR.x
  653. || thepath->start.p.y < boxes[0].LL.y
  654. || thepath->start.p.y > boxes[0].UR.y) {
  655. if (Verbose) {
  656. fprintf(stderr, "in checkpath, start port not in first box\n");
  657. printpath(thepath);
  658. }
  659. thepath->start.p.x = fmax(thepath->start.p.x, boxes[0].LL.x);
  660. thepath->start.p.x = fmin(thepath->start.p.x, boxes[0].UR.x);
  661. thepath->start.p.y = fmax(thepath->start.p.y, boxes[0].LL.y);
  662. thepath->start.p.y = fmin(thepath->start.p.y, boxes[0].UR.y);
  663. }
  664. if (thepath->end.p.x < boxes[boxn - 1].LL.x
  665. || thepath->end.p.x > boxes[boxn - 1].UR.x
  666. || thepath->end.p.y < boxes[boxn - 1].LL.y
  667. || thepath->end.p.y > boxes[boxn - 1].UR.y) {
  668. if (Verbose) {
  669. fprintf(stderr, "in checkpath, end port not in last box\n");
  670. printpath(thepath);
  671. }
  672. thepath->end.p.x = fmax(thepath->end.p.x, boxes[boxn - 1].LL.x);
  673. thepath->end.p.x = fmin(thepath->end.p.x, boxes[boxn - 1].UR.x);
  674. thepath->end.p.y = fmax(thepath->end.p.y, boxes[boxn - 1].LL.y);
  675. thepath->end.p.y = fmin(thepath->end.p.y, boxes[boxn - 1].UR.y);
  676. }
  677. return 0;
  678. }
  679. static void printpath(path * pp)
  680. {
  681. fprintf(stderr, "%" PRISIZE_T " boxes:\n", pp->nbox);
  682. for (size_t bi = 0; bi < pp->nbox; bi++)
  683. fprintf(stderr, "%" PRISIZE_T " (%.5g, %.5g), (%.5g, %.5g)\n", bi,
  684. pp->boxes[bi].LL.x, pp->boxes[bi].LL.y,
  685. pp->boxes[bi].UR.x, pp->boxes[bi].UR.y);
  686. fprintf(stderr, "start port: (%.5g, %.5g), tangent angle: %.5g, %s\n",
  687. pp->start.p.x, pp->start.p.y, pp->start.theta,
  688. pp->start.constrained ? "constrained" : "not constrained");
  689. fprintf(stderr, "end port: (%.5g, %.5g), tangent angle: %.5g, %s\n",
  690. pp->end.p.x, pp->end.p.y, pp->end.theta,
  691. pp->end.constrained ? "constrained" : "not constrained");
  692. }
  693. static pointf get_centroid(Agraph_t *g)
  694. {
  695. pointf sum = {0.0, 0.0};
  696. sum.x = (GD_bb(g).LL.x + GD_bb(g).UR.x) / 2.0;
  697. sum.y = (GD_bb(g).LL.y + GD_bb(g).UR.y) / 2.0;
  698. return sum;
  699. }
  700. DEFINE_LIST(nodes, node_t *)
  701. static void nodes_delete(nodes_t *pvec) {
  702. if (pvec != NULL) {
  703. nodes_free(pvec);
  704. }
  705. free(pvec);
  706. }
  707. DEFINE_LIST_WITH_DTOR(cycles, nodes_t *, nodes_delete)
  708. static bool cycle_contains_edge(nodes_t *cycle, edge_t *edge) {
  709. node_t* start = agtail(edge);
  710. node_t* end = aghead(edge);
  711. const size_t cycle_len = nodes_size(cycle);
  712. for (size_t i=0; i < cycle_len; ++i) {
  713. const node_t *c_start = nodes_get(cycle, i == 0 ? cycle_len - 1 : i - 1);
  714. const node_t *c_end = nodes_get(cycle, i);
  715. if (c_start == start && c_end == end)
  716. return true;
  717. }
  718. return false;
  719. }
  720. static bool eq(const node_t *a, const node_t *b) { return a == b; }
  721. static bool is_cycle_unique(cycles_t *cycles, nodes_t *cycle) {
  722. const size_t cycle_len = nodes_size(cycle);
  723. size_t i; //node counter
  724. bool all_items_match;
  725. for (size_t c = 0; c < cycles_size(cycles); ++c) {
  726. nodes_t *cur_cycle = cycles_get(cycles, c);
  727. const size_t cur_cycle_len = nodes_size(cur_cycle);
  728. //if all the items match in equal length cycles then we're not unique
  729. if (cur_cycle_len == cycle_len) {
  730. all_items_match = true;
  731. for (i=0; i < cur_cycle_len; ++i) {
  732. node_t *cur_cycle_item = nodes_get(cur_cycle, i);
  733. if (!nodes_contains(cycle, cur_cycle_item, eq)) {
  734. all_items_match = false;
  735. break;
  736. }
  737. }
  738. if (all_items_match)
  739. return false;
  740. }
  741. }
  742. return true;
  743. }
  744. static void dfs(graph_t *g, node_t *search, nodes_t *visited, node_t *end,
  745. cycles_t *cycles) {
  746. edge_t* e;
  747. node_t* n;
  748. if (nodes_contains(visited, search, eq)) {
  749. if (search == end) {
  750. if (is_cycle_unique(cycles, visited)) {
  751. nodes_t *cycle = gv_alloc(sizeof(nodes_t));
  752. *cycle = nodes_copy(visited);
  753. cycles_append(cycles, cycle);
  754. }
  755. }
  756. } else {
  757. nodes_append(visited, search);
  758. for (e = agfstout(g, search); e; e = agnxtout(g, e)) {
  759. n = aghead(e);
  760. dfs(g, n, visited, end, cycles);
  761. }
  762. if (!nodes_is_empty(visited)) {
  763. (void)nodes_pop_back(visited);
  764. }
  765. }
  766. }
  767. // Returns a vec of vec of nodes (aka a vector of cycles)
  768. static cycles_t find_all_cycles(graph_t *g) {
  769. node_t *n;
  770. // vector of vectors of nodes -- AKA cycles to delete
  771. cycles_t alloced_cycles = {0};
  772. cycles_t cycles = {0}; // vector of vectors of nodes AKA a vector of cycles
  773. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  774. nodes_t *cycle = gv_alloc(sizeof(nodes_t));
  775. // keep track of all items we allocate to clean up at the end of this function
  776. cycles_append(&alloced_cycles, cycle);
  777. dfs(g, n, cycle, n, &cycles);
  778. }
  779. cycles_free(&alloced_cycles); // cycles contains copied vecs
  780. return cycles;
  781. }
  782. static nodes_t *find_shortest_cycle_with_edge(cycles_t *cycles, edge_t *edge,
  783. size_t min_size) {
  784. nodes_t *shortest = NULL;
  785. for (size_t c = 0; c < cycles_size(cycles); ++c) {
  786. nodes_t *cycle = cycles_get(cycles, c);
  787. size_t cycle_len = nodes_size(cycle);
  788. if (cycle_len < min_size)
  789. continue;
  790. if (shortest == NULL || nodes_size(shortest) > cycle_len) {
  791. if (cycle_contains_edge(cycle, edge)) {
  792. shortest = cycle;
  793. }
  794. }
  795. }
  796. return shortest;
  797. }
  798. static pointf get_cycle_centroid(graph_t *g, edge_t* edge)
  799. {
  800. cycles_t cycles = find_all_cycles(g);
  801. //find the center of the shortest cycle containing this edge
  802. //cycles of length 2 do their own thing, we want 3 or
  803. nodes_t *cycle = find_shortest_cycle_with_edge(&cycles, edge, 3);
  804. pointf sum = {0.0, 0.0};
  805. if (cycle == NULL) {
  806. cycles_free(&cycles);
  807. return get_centroid(g);
  808. }
  809. double cnt = 0;
  810. for (size_t idx = 0; idx < nodes_size(cycle); ++idx) {
  811. node_t *n = nodes_get(cycle, idx);
  812. sum.x += ND_coord(n).x;
  813. sum.y += ND_coord(n).y;
  814. cnt++;
  815. }
  816. cycles_free(&cycles);
  817. sum.x /= cnt;
  818. sum.y /= cnt;
  819. return sum;
  820. }
  821. static void bend(pointf spl[4], pointf centroid)
  822. {
  823. pointf a;
  824. double r;
  825. pointf midpt = mid_pointf(spl[0], spl[3]);
  826. double dist = DIST(spl[3], spl[0]);
  827. r = dist/5.0;
  828. {
  829. double vX = centroid.x - midpt.x;
  830. double vY = centroid.y - midpt.y;
  831. double magV = hypot(vX, vY);
  832. if (magV == 0) return; /* if midpoint == centroid, don't divide by zero */
  833. a.x = midpt.x - vX / magV * r; /* + would be closest point */
  834. a.y = midpt.y - vY / magV * r;
  835. }
  836. /* this can be improved */
  837. spl[1].x = spl[2].x = a.x;
  838. spl[1].y = spl[2].y = a.y;
  839. }
  840. // FIX: handle ports on boundary?
  841. void
  842. makeStraightEdge(graph_t * g, edge_t * e, int et, splineInfo* sinfo)
  843. {
  844. edge_t *e0;
  845. size_t e_cnt = 1;
  846. e0 = e;
  847. while (e0 != ED_to_virt(e0) && (e0 = ED_to_virt(e0))) e_cnt++;
  848. edge_t **edge_list = gv_calloc(e_cnt, sizeof(edge_t *));
  849. e0 = e;
  850. for (size_t i = 0; i < e_cnt; i++) {
  851. edge_list[i] = e0;
  852. e0 = ED_to_virt(e0);
  853. }
  854. assert(e_cnt <= INT_MAX);
  855. makeStraightEdges(g, edge_list, e_cnt, et, sinfo);
  856. free(edge_list);
  857. }
  858. void makeStraightEdges(graph_t *g, edge_t **edge_list, size_t e_cnt, int et,
  859. splineInfo *sinfo) {
  860. pointf dumb[4];
  861. bool curved = et == EDGETYPE_CURVED;
  862. pointf del;
  863. edge_t *e = edge_list[0];
  864. node_t *n = agtail(e);
  865. node_t *head = aghead(e);
  866. dumb[1] = dumb[0] = add_pointf(ND_coord(n), ED_tail_port(e).p);
  867. dumb[2] = dumb[3] = add_pointf(ND_coord(head), ED_head_port(e).p);
  868. if (e_cnt == 1 || Concentrate) {
  869. if (curved) bend(dumb,get_cycle_centroid(g, edge_list[0]));
  870. clip_and_install(e, aghead(e), dumb, 4, sinfo);
  871. addEdgeLabels(e);
  872. return;
  873. }
  874. if (APPROXEQPT(dumb[0], dumb[3], MILLIPOINT)) {
  875. /* degenerate case */
  876. dumb[1] = dumb[0];
  877. dumb[2] = dumb[3];
  878. del.x = 0;
  879. del.y = 0;
  880. }
  881. else {
  882. pointf perp = {
  883. .x = dumb[0].y - dumb[3].y,
  884. .y = dumb[3].x - dumb[0].x
  885. };
  886. double l_perp = hypot(perp.x, perp.y);
  887. int xstep = GD_nodesep(g->root);
  888. assert(e_cnt - 1 <= INT_MAX);
  889. int dx = xstep * (int)(e_cnt - 1) / 2;
  890. dumb[1].x = dumb[0].x + dx * perp.x / l_perp;
  891. dumb[1].y = dumb[0].y + dx * perp.y / l_perp;
  892. dumb[2].x = dumb[3].x + dx * perp.x / l_perp;
  893. dumb[2].y = dumb[3].y + dx * perp.y / l_perp;
  894. del.x = -xstep * perp.x / l_perp;
  895. del.y = -xstep * perp.y / l_perp;
  896. }
  897. for (size_t i = 0; i < e_cnt; i++) {
  898. edge_t *e0 = edge_list[i];
  899. pointf dumber[4];
  900. if (aghead(e0) == head) {
  901. for (size_t j = 0; j < 4; j++) {
  902. dumber[j] = dumb[j];
  903. }
  904. } else {
  905. for (size_t j = 0; j < 4; j++) {
  906. dumber[3 - j] = dumb[j];
  907. }
  908. }
  909. if (et == EDGETYPE_PLINE) {
  910. Ppoint_t pts[] = {dumber[0], dumber[1], dumber[2], dumber[3]};
  911. Ppolyline_t spl, line = {.pn = 4, .ps = pts};
  912. make_polyline (line, &spl);
  913. clip_and_install(e0, aghead(e0), spl.ps, (size_t)spl.pn, sinfo);
  914. }
  915. else
  916. clip_and_install(e0, aghead(e0), dumber, 4, sinfo);
  917. addEdgeLabels(e0);
  918. dumb[1] = add_pointf(dumb[1], del);
  919. dumb[2] = add_pointf(dumb[2], del);
  920. }
  921. }