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