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