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