Graphviz 16.1.1~dev.20261101.1716
Loading...
Searching...
No Matches
route.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <assert.h>
14#include <limits.h>
15#include <math.h>
16#include <pathplan/pathutil.h>
17#include <pathplan/solvers.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <util/list.h>
21
22#define EPSILON1 1E-3
23#define EPSILON2 1E-6
24
25typedef struct tna_t {
26 double t;
29
30#define DISTSQ(a, b) \
31 ((((a).x - (b).x) * ((a).x - (b).x)) + (((a).y - (b).y) * ((a).y - (b).y)))
32
33typedef LIST(Ppoint_t) ops_t;
34
35static ops_t ops;
36
37static int reallyroutespline(Pedge_t *, size_t, Ppoint_t *, int, Ppoint_t,
38 Ppoint_t);
39static int mkspline(Ppoint_t *, int, const tna_t *, Ppoint_t, Ppoint_t,
41static int splinefits(Pedge_t *, size_t, Ppoint_t, Pvector_t, Ppoint_t,
42 Pvector_t, Ppoint_t *, int);
43static int splineisinside(Pedge_t *, size_t, Ppoint_t *);
44static int splineintersectsline(Ppoint_t *, Ppoint_t *, double *);
45static void points2coeff(double, double, double, double, double *);
46static void addroot(double, double *, int *);
47
49
52static double dist(Ppoint_t, Ppoint_t);
53static Ppoint_t scale(Ppoint_t, double);
54static double dot(Ppoint_t, Ppoint_t);
55static double B0(double t);
56static double B1(double t);
57static double B2(double t);
58static double B3(double t);
59static double B01(double t);
60static double B23(double t);
61
62/* Given a set of barrier line segments edges as obstacles, a template
63 * path input_route, and endpoint vectors endpoint_slopes, construct a spline
64 * fitting the input and endpoint vectors, and return in output_route.
65 * Return 0 on success and -1 on failure, including no memory.
66 */
67int Proutespline(Pedge_t *barriers, size_t n_barriers, Ppolyline_t input_route,
68 Ppoint_t endpoint_slopes[2], Ppolyline_t *output_route) {
69 Ppoint_t *inps;
70 int inpn;
71
72 /* unpack into previous format rather than modify legacy code */
73 inps = input_route.ps;
74 assert(input_route.pn <= INT_MAX);
75 inpn = (int)input_route.pn;
76
77 /* generate the splines */
78 endpoint_slopes[0] = normv(endpoint_slopes[0]);
79 endpoint_slopes[1] = normv(endpoint_slopes[1]);
80 LIST_CLEAR(&ops);
81 if (!LIST_TRY_APPEND(&ops, inps[0])) {
82 return -1;
83 }
84 if (reallyroutespline(barriers, n_barriers, inps, inpn, endpoint_slopes[0],
85 endpoint_slopes[1]) == -1)
86 return -1;
87 output_route->pn = LIST_SIZE(&ops);
88 output_route->ps = LIST_FRONT(&ops);
89
90 return 0;
91}
92
93static int reallyroutespline(Pedge_t *edges, size_t edgen, Ppoint_t *inps,
94 int inpn, Ppoint_t ev0, Ppoint_t ev1) {
95 Ppoint_t p1, p2;
96 Pvector_t v1, v2;
97 double d;
98
99 assert(inpn > 0);
100 tna_t *const tnas = calloc((size_t)inpn, sizeof(tna_t));
101 if (tnas == NULL) {
102 return -1;
103 }
104 tnas[0].t = 0;
105 for (int i = 1; i < inpn; i++)
106 tnas[i].t = tnas[i - 1].t + dist(inps[i], inps[i - 1]);
107 for (int i = 1; i < inpn; i++)
108 tnas[i].t /= tnas[inpn - 1].t;
109 for (int i = 0; i < inpn; i++) {
110 tnas[i].a[0] = scale(ev0, B1(tnas[i].t));
111 tnas[i].a[1] = scale(ev1, B2(tnas[i].t));
112 }
113 if (mkspline(inps, inpn, tnas, ev0, ev1, &p1, &v1, &p2, &v2) == -1) {
114 free(tnas);
115 return -1;
116 }
117 int fit = splinefits(edges, edgen, p1, v1, p2, v2, inps, inpn);
118 if (fit > 0) {
119 free(tnas);
120 return 0;
121 }
122 if (fit < 0) {
123 free(tnas);
124 return -1;
125 }
126 const Ppoint_t cp1 = add(p1, scale(v1, 1 / 3.0));
127 const Ppoint_t cp2 = sub(p2, scale(v2, 1 / 3.0));
128 int maxi = -1;
129 double maxd = -1;
130 for (int i = 1; i < inpn - 1; i++) {
131 const double t = tnas[i].t;
132 const Ppoint_t p = {
133 .x = B0(t) * p1.x + B1(t) * cp1.x + B2(t) * cp2.x + B3(t) * p2.x,
134 .y = B0(t) * p1.y + B1(t) * cp1.y + B2(t) * cp2.y + B3(t) * p2.y};
135 if ((d = dist(p, inps[i])) > maxd) {
136 maxd = d;
137 maxi = i;
138 }
139 }
140 free(tnas);
141 const int spliti = maxi;
142 const Pvector_t splitv1 = normv(sub(inps[spliti], inps[spliti - 1]));
143 const Pvector_t splitv2 = normv(sub(inps[spliti + 1], inps[spliti]));
144 const Pvector_t splitv = normv(add(splitv1, splitv2));
145 if (reallyroutespline(edges, edgen, inps, spliti + 1, ev0, splitv) < 0) {
146 return -1;
147 }
148 if (reallyroutespline(edges, edgen, &inps[spliti], inpn - spliti, splitv,
149 ev1) < 0) {
150 return -1;
151 }
152 return 0;
153}
154
155static int mkspline(Ppoint_t *inps, int inpn, const tna_t *tnas, Ppoint_t ev0,
156 Ppoint_t ev1, Ppoint_t *sp0, Ppoint_t *sv0, Ppoint_t *sp1,
157 Ppoint_t *sv1) {
158 Ppoint_t tmp;
159 double c[2][2], x[2], det01, det0X, detX1;
160 double d01, scale0, scale3;
161 int i;
162
163 scale0 = scale3 = 0.0;
164 c[0][0] = c[0][1] = c[1][0] = c[1][1] = 0.0;
165 x[0] = x[1] = 0.0;
166 for (i = 0; i < inpn; i++) {
167 c[0][0] += dot(tnas[i].a[0], tnas[i].a[0]);
168 c[0][1] += dot(tnas[i].a[0], tnas[i].a[1]);
169 c[1][0] = c[0][1];
170 c[1][1] += dot(tnas[i].a[1], tnas[i].a[1]);
171 tmp = sub(inps[i], add(scale(inps[0], B01(tnas[i].t)),
172 scale(inps[inpn - 1], B23(tnas[i].t))));
173 x[0] += dot(tnas[i].a[0], tmp);
174 x[1] += dot(tnas[i].a[1], tmp);
175 }
176 det01 = c[0][0] * c[1][1] - c[1][0] * c[0][1];
177 det0X = c[0][0] * x[1] - c[0][1] * x[0];
178 detX1 = x[0] * c[1][1] - x[1] * c[0][1];
179 if (fabs(det01) >= 1e-6) {
180 scale0 = detX1 / det01;
181 scale3 = det0X / det01;
182 }
183 if (fabs(det01) < 1e-6 || scale0 <= 0.0 || scale3 <= 0.0) {
184 d01 = dist(inps[0], inps[inpn - 1]) / 3.0;
185 scale0 = d01;
186 scale3 = d01;
187 }
188 *sp0 = inps[0];
189 *sv0 = scale(ev0, scale0);
190 *sp1 = inps[inpn - 1];
191 *sv1 = scale(ev1, scale3);
192 return 0;
193}
194
195static double dist_n(Ppoint_t *p, int n) {
196 int i;
197 double rv;
198
199 rv = 0.0;
200 for (i = 1; i < n; i++) {
201 rv += hypot(p[i].x - p[i - 1].x, p[i].y - p[i - 1].y);
202 }
203 return rv;
204}
205
206static int splinefits(Pedge_t *edges, size_t edgen, Ppoint_t pa, Pvector_t va,
207 Ppoint_t pb, Pvector_t vb, Ppoint_t *inps, int inpn) {
208 Ppoint_t sps[4];
209 double a;
210 int pi;
211 int forceflag;
212 int first = 1;
213
214 forceflag = (inpn == 2 ? 1 : 0);
215
216 a = 4;
217 for (;;) {
218 sps[0].x = pa.x;
219 sps[0].y = pa.y;
220 sps[1].x = pa.x + a * va.x / 3.0;
221 sps[1].y = pa.y + a * va.y / 3.0;
222 sps[2].x = pb.x - a * vb.x / 3.0;
223 sps[2].y = pb.y - a * vb.y / 3.0;
224 sps[3].x = pb.x;
225 sps[3].y = pb.y;
226
227 /* shortcuts (paths shorter than the shortest path) not allowed -
228 * they must be outside the constraint polygon. this can happen
229 * if the candidate spline intersects the constraint polygon exactly
230 * on sides or vertices. maybe this could be more elegant, but
231 * it solves the immediate problem. we could also try jittering the
232 * constraint polygon, or computing the candidate spline more carefully,
233 * for example using the path. SCN */
234
235 if (first && (dist_n(sps, 4) < (dist_n(inps, inpn) - EPSILON1)))
236 return 0;
237 first = 0;
238
239 if (splineisinside(edges, edgen, &sps[0])) {
240 for (pi = 1; pi < 4; pi++) {
241 if (!LIST_TRY_APPEND(&ops, sps[pi])) {
242 return -1;
243 }
244 }
245#if defined(DEBUG) && DEBUG >= 1
246 fprintf(stderr, "success: %f %f\n", a, a);
247#endif
248 return 1;
249 }
250 // is `a` 0, accounting for the precision with which it was computed (on the
251 // last loop iteration) below?
252 if (a < 0.005) {
253 if (forceflag) {
254 for (pi = 1; pi < 4; pi++) {
255 if (!LIST_TRY_APPEND(&ops, sps[pi])) {
256 return -1;
257 }
258 }
259#if defined(DEBUG) && DEBUG >= 1
260 fprintf(stderr, "forced straight line: %f %f\n", a, a);
261#endif
262 return 1;
263 }
264 break;
265 }
266 if (a > .01)
267 a /= 2;
268 else
269 a = 0;
270 }
271#if defined(DEBUG) && DEBUG >= 1
272 fprintf(stderr, "failure\n");
273#endif
274 return 0;
275}
276
277static int splineisinside(Pedge_t *edges, size_t edgen, Ppoint_t *sps) {
278 double roots[4];
279 int rooti, rootn;
280 Ppoint_t lps[2], ip;
281 double t, ta, tb, tc, td;
282
283 for (size_t ei = 0; ei < edgen; ei++) {
284 lps[0] = edges[ei].a, lps[1] = edges[ei].b;
285 if ((rootn = splineintersectsline(sps, lps, roots)) == 4)
286 continue;
287 for (rooti = 0; rooti < rootn; rooti++) {
288 if (roots[rooti] < EPSILON2 || roots[rooti] > 1 - EPSILON2)
289 continue;
290 t = roots[rooti];
291 td = t * t * t;
292 tc = 3 * t * t * (1 - t);
293 tb = 3 * t * (1 - t) * (1 - t);
294 ta = (1 - t) * (1 - t) * (1 - t);
295 ip.x = ta * sps[0].x + tb * sps[1].x + tc * sps[2].x + td * sps[3].x;
296 ip.y = ta * sps[0].y + tb * sps[1].y + tc * sps[2].y + td * sps[3].y;
297 if (DISTSQ(ip, lps[0]) < EPSILON1 || DISTSQ(ip, lps[1]) < EPSILON1)
298 continue;
299 return 0;
300 }
301 }
302 return 1;
303}
304
305static int splineintersectsline(Ppoint_t *sps, Ppoint_t *lps, double *roots) {
306 double scoeff[4], xcoeff[2], ycoeff[2];
307 double xroots[3], yroots[3], tv, sv, rat;
308 int rootn, xrootn, yrootn, i, j;
309
310 xcoeff[0] = lps[0].x;
311 xcoeff[1] = lps[1].x - lps[0].x;
312 ycoeff[0] = lps[0].y;
313 ycoeff[1] = lps[1].y - lps[0].y;
314 rootn = 0;
315 if (xcoeff[1] == 0) {
316 if (ycoeff[1] == 0) {
317 points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, scoeff);
318 scoeff[0] -= xcoeff[0];
319 xrootn = solve3(scoeff, xroots);
320 points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, scoeff);
321 scoeff[0] -= ycoeff[0];
322 yrootn = solve3(scoeff, yroots);
323 if (xrootn == 4)
324 if (yrootn == 4)
325 return 4;
326 else
327 for (j = 0; j < yrootn; j++)
328 addroot(yroots[j], roots, &rootn);
329 else if (yrootn == 4)
330 for (i = 0; i < xrootn; i++)
331 addroot(xroots[i], roots, &rootn);
332 else
333 for (i = 0; i < xrootn; i++)
334 for (j = 0; j < yrootn; j++)
335 if (xroots[i] == yroots[j])
336 addroot(xroots[i], roots, &rootn);
337 return rootn;
338 } else {
339 points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, scoeff);
340 scoeff[0] -= xcoeff[0];
341 xrootn = solve3(scoeff, xroots);
342 if (xrootn == 4)
343 return 4;
344 for (i = 0; i < xrootn; i++) {
345 tv = xroots[i];
346 if (tv >= 0 && tv <= 1) {
347 points2coeff(sps[0].y, sps[1].y, sps[2].y, sps[3].y, scoeff);
348 sv = scoeff[0] + tv * (scoeff[1] + tv * (scoeff[2] + tv * scoeff[3]));
349 sv = (sv - ycoeff[0]) / ycoeff[1];
350 if ((0 <= sv) && (sv <= 1))
351 addroot(tv, roots, &rootn);
352 }
353 }
354 return rootn;
355 }
356 } else {
357 rat = ycoeff[1] / xcoeff[1];
358 points2coeff(sps[0].y - rat * sps[0].x, sps[1].y - rat * sps[1].x,
359 sps[2].y - rat * sps[2].x, sps[3].y - rat * sps[3].x, scoeff);
360 scoeff[0] += rat * xcoeff[0] - ycoeff[0];
361 xrootn = solve3(scoeff, xroots);
362 if (xrootn == 4)
363 return 4;
364 for (i = 0; i < xrootn; i++) {
365 tv = xroots[i];
366 if (tv >= 0 && tv <= 1) {
367 points2coeff(sps[0].x, sps[1].x, sps[2].x, sps[3].x, scoeff);
368 sv = scoeff[0] + tv * (scoeff[1] + tv * (scoeff[2] + tv * scoeff[3]));
369 sv = (sv - xcoeff[0]) / xcoeff[1];
370 if ((0 <= sv) && (sv <= 1))
371 addroot(tv, roots, &rootn);
372 }
373 }
374 return rootn;
375 }
376}
377
378static void points2coeff(double v0, double v1, double v2, double v3,
379 double *coeff) {
380 coeff[3] = v3 + 3 * v1 - (v0 + 3 * v2);
381 coeff[2] = 3 * v0 + 3 * v2 - 6 * v1;
382 coeff[1] = 3 * (v1 - v0);
383 coeff[0] = v0;
384}
385
386static void addroot(double root, double *roots, int *rootnp) {
387 if (root >= 0 && root <= 1)
388 roots[*rootnp] = root, (*rootnp)++;
389}
390
392 double d;
393
394 d = v.x * v.x + v.y * v.y;
395 if (d > 1e-6) {
396 d = sqrt(d);
397 v.x /= d, v.y /= d;
398 }
399 return v;
400}
401
403 p1.x += p2.x, p1.y += p2.y;
404 return p1;
405}
406
408 p1.x -= p2.x, p1.y -= p2.y;
409 return p1;
410}
411
412static double dist(Ppoint_t p1, Ppoint_t p2) {
413 double dx, dy;
414
415 dx = p2.x - p1.x, dy = p2.y - p1.y;
416 return hypot(dx, dy);
417}
418
419static Ppoint_t scale(Ppoint_t p, double c) {
420 p.x *= c, p.y *= c;
421 return p;
422}
423
424static double dot(Ppoint_t p1, Ppoint_t p2) {
425 return p1.x * p2.x + p1.y * p2.y;
426}
427
428static double B0(double t) {
429 double tmp = 1.0 - t;
430 return tmp * tmp * tmp;
431}
432
433static double B1(double t) {
434 double tmp = 1.0 - t;
435 return 3 * t * tmp * tmp;
436}
437
438static double B2(double t) {
439 double tmp = 1.0 - t;
440 return 3 * t * t * tmp;
441}
442
443static double B3(double t) { return t * t * t; }
444
445static double B01(double t) {
446 double tmp = 1.0 - t;
447 return tmp * tmp * (tmp + 3 * t);
448}
449
450static double B23(double t) {
451 double tmp = 1.0 - t;
452 return t * t * (3 * tmp + t);
453}
#define sub(h, i)
Definition closest.c:62
static float dy
Definition draw.c:43
static float dx
Definition draw.c:42
#define dot(v, w)
Definition geom.c:191
void free(void *)
node NULL
Definition grammar.y:181
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_CLEAR(list)
Definition list.h:307
#define LIST_FRONT(list)
Definition list.h:222
#define LIST_TRY_APPEND(list, item)
Definition list.h:118
int Proutespline(Pedge_t *barriers, size_t n_barriers, Ppolyline_t input_route, Pvector_t endpoint_slopes[2], Ppolyline_t *output_route)
static int splineisinside(Pedge_t *edges, size_t edgen, Ppoint_t *sps)
Definition route.c:277
#define EPSILON2
Definition route.c:23
static double B23(double t)
Definition route.c:450
static int splineintersectsline(Ppoint_t *sps, Ppoint_t *lps, double *roots)
Definition route.c:305
#define DISTSQ(a, b)
Definition route.c:30
static double B01(double t)
Definition route.c:445
static double B1(double t)
Definition route.c:433
static double B0(double t)
Definition route.c:428
static double dist(Ppoint_t p1, Ppoint_t p2)
Definition route.c:412
static double B3(double t)
Definition route.c:443
static double dist_n(Ppoint_t *p, int n)
Definition route.c:195
static Ppoint_t add(Ppoint_t p1, Ppoint_t p2)
Definition route.c:402
static void addroot(double root, double *roots, int *rootnp)
Definition route.c:386
static Ppoint_t scale(Ppoint_t p, double c)
Definition route.c:419
static double B2(double t)
Definition route.c:438
static int splinefits(Pedge_t *edges, size_t edgen, Ppoint_t pa, Pvector_t va, Ppoint_t pb, Pvector_t vb, Ppoint_t *inps, int inpn)
Definition route.c:206
#define EPSILON1
Definition route.c:22
static int mkspline(Ppoint_t *inps, int inpn, const tna_t *tnas, Ppoint_t ev0, Ppoint_t ev1, Ppoint_t *sp0, Ppoint_t *sv0, Ppoint_t *sp1, Ppoint_t *sv1)
Definition route.c:155
static Pvector_t normv(Pvector_t v)
Definition route.c:391
static int reallyroutespline(Pedge_t *edges, size_t edgen, Ppoint_t *inps, int inpn, Ppoint_t ev0, Ppoint_t ev1)
Definition route.c:93
static void points2coeff(double v0, double v1, double v2, double v3, double *coeff)
Definition route.c:378
int solve3(double *coeff, double *roots)
Definition solvers.c:26
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 route.c:25
double t
Definition route.c:26
Ppoint_t a[2]
Definition route.c:27