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