Graphviz 16.1.1~dev.20261012.0359
Loading...
Searching...
No Matches
shortest.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 <math.h>
14#include <pathplan/pathutil.h>
15#include <pathplan/tri.h>
16#include <stdbool.h>
17#include <stdint.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <util/list.h>
21#include <util/prisize_t.h>
22
23#define DQ_FRONT 1
24#define DQ_BACK 2
25
26#define prerror(msg) \
27 fprintf(stderr, "lib/pathplan/%s:%d: %s\n", __FILE__, __LINE__, (msg))
28
29#define POINTSIZE sizeof(Ppoint_t)
30
35
36#define POINTNLINKSIZE sizeof(pointnlink_t)
37#define POINTNLINKPSIZE sizeof(pointnlink_t *)
38
44
45typedef struct triangle_t {
46 int mark;
49
54
55typedef LIST(triangle_t) triangles_t;
56
57static Ppoint_t *ops;
58static size_t opn;
59
60static int triangulate(triangles_t *tris, pointnlink_t **, size_t);
61static int loadtriangle(triangles_t *tris, pointnlink_t *, pointnlink_t *,
62 pointnlink_t *);
63static void connecttris(triangles_t *tris, size_t, size_t);
64static bool marktripath(triangles_t *tris, size_t, size_t);
65
66static void add2dq(deque_t *dq, int, pointnlink_t *);
67static void splitdq(deque_t *dq, int, size_t);
68static size_t finddqsplit(const deque_t *dq, pointnlink_t *);
69
70static int pointintri(const triangles_t tris, size_t, Ppoint_t *);
71
72static int growops(size_t);
73
74static Ppoint_t point_indexer(void *base, size_t index) {
75 pointnlink_t **b = base;
76 return *b[index]->pp;
77}
78
79/* Pshortestpath:
80 * Find a shortest path contained in the polygon polyp going between the
81 * points supplied in eps. The resulting polyline is stored in output.
82 * Return 0 on success, -1 on bad input, -2 on memory allocation problem.
83 */
84int Pshortestpath(Ppoly_t *polyp, Ppoint_t eps[2], Ppolyline_t *output) {
85 size_t pi, minpi;
86 double minx;
87 size_t trii, trij, ftrii, ltrii;
88 int ei;
89 pointnlink_t epnls[2], *lpnlp, *rpnlp, *pnlp;
90 triangle_t *trip;
91
92 /* make space */
93 pointnlink_t *pnls = calloc(polyp->pn, sizeof(pnls[0]));
94 if (polyp->pn > 0 && pnls == NULL) {
95 prerror("cannot realloc pnls");
96 return -2;
97 }
98 pointnlink_t **pnlps = calloc(polyp->pn, sizeof(pnlps[0]));
99 if (polyp->pn > 0 && pnlps == NULL) {
100 prerror("cannot realloc pnlps");
101 free(pnls);
102 return -2;
103 }
104 size_t pnll = 0;
105 triangles_t tris = {0};
106
107 deque_t dq = {.pnlpn = polyp->pn * 2};
108 dq.pnlps = calloc(dq.pnlpn, POINTNLINKPSIZE);
109 if (dq.pnlps == NULL) {
110 prerror("cannot realloc dq.pnls");
111 free(pnlps);
112 free(pnls);
113 return -2;
114 }
115 dq.fpnlpi = dq.pnlpn / 2;
116 dq.lpnlpi = dq.fpnlpi - 1;
117
118 /* make sure polygon is CCW and load pnls array */
119 for (pi = 0, minx = HUGE_VAL, minpi = SIZE_MAX; pi < polyp->pn; pi++) {
120 if (minx > polyp->ps[pi].x)
121 minx = polyp->ps[pi].x, minpi = pi;
122 }
123 const Ppoint_t p2 = polyp->ps[minpi];
124 const Ppoint_t p1 = polyp->ps[minpi == 0 ? polyp->pn - 1 : minpi - 1];
125 const Ppoint_t p3 = polyp->ps[(minpi + 1) % polyp->pn];
126 if ((p1.x == p2.x && p2.x == p3.x && p3.y > p2.y) ||
127 ccw(p1, p2, p3) != ISCCW) {
128 for (pi = polyp->pn - 1; pi != SIZE_MAX; pi--) {
129 if (pi < polyp->pn - 1 && polyp->ps[pi].x == polyp->ps[pi + 1].x &&
130 polyp->ps[pi].y == polyp->ps[pi + 1].y)
131 continue;
132 pnls[pnll].pp = &polyp->ps[pi];
133 pnls[pnll].link = &pnls[pnll % polyp->pn];
134 pnlps[pnll] = &pnls[pnll];
135 pnll++;
136 }
137 } else {
138 for (pi = 0; pi < polyp->pn; pi++) {
139 if (pi > 0 && polyp->ps[pi].x == polyp->ps[pi - 1].x &&
140 polyp->ps[pi].y == polyp->ps[pi - 1].y)
141 continue;
142 pnls[pnll].pp = &polyp->ps[pi];
143 pnls[pnll].link = &pnls[pnll % polyp->pn];
144 pnlps[pnll] = &pnls[pnll];
145 pnll++;
146 }
147 }
148
149#if defined(DEBUG) && DEBUG >= 1
150 fprintf(stderr, "points\n%" PRISIZE_T "\n", pnll);
151 for (size_t pnli = 0; pnli < pnll; pnli++)
152 fprintf(stderr, "%f %f\n", pnls[pnli].pp->x, pnls[pnli].pp->y);
153#endif
154
155 /* generate list of triangles */
156 if (triangulate(&tris, pnlps, pnll)) {
157 LIST_FREE(&tris);
158 free(dq.pnlps);
159 free(pnlps);
160 free(pnls);
161 return -2;
162 }
163
164#if defined(DEBUG) && DEBUG >= 2
165 fprintf(stderr, "triangles\n%" PRISIZE_T "\n", LIST_SIZE(&tris));
166 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
167 for (ei = 0; ei < 3; ei++)
168 fprintf(stderr, "%f %f\n", LIST_GET(&tris, trii).e[ei].pnl0p->pp->x,
169 LIST_GET(&tris, trii).e[ei].pnl0p->pp->y);
170#endif
171
172 /* connect all pairs of triangles that share an edge */
173 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
174 for (trij = trii + 1; trij < LIST_SIZE(&tris); trij++)
175 connecttris(&tris, trii, trij);
176
177 /* find first and last triangles */
178 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
179 if (pointintri(tris, trii, &eps[0]))
180 break;
181 if (trii == LIST_SIZE(&tris)) {
182 prerror("source point not in any triangle");
183 LIST_FREE(&tris);
184 free(dq.pnlps);
185 free(pnlps);
186 free(pnls);
187 return -1;
188 }
189 ftrii = trii;
190 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
191 if (pointintri(tris, trii, &eps[1]))
192 break;
193 if (trii == LIST_SIZE(&tris)) {
194 prerror("destination point not in any triangle");
195 LIST_FREE(&tris);
196 free(dq.pnlps);
197 free(pnlps);
198 free(pnls);
199 return -1;
200 }
201 ltrii = trii;
202
203 /* mark the strip of triangles from eps[0] to eps[1] */
204 if (!marktripath(&tris, ftrii, ltrii)) {
205 prerror("cannot find triangle path");
206 LIST_FREE(&tris);
207 free(dq.pnlps);
208 free(pnlps);
209 free(pnls);
210 /* a straight line is better than failing */
211 if (growops(2) != 0)
212 return -2;
213 output->pn = 2;
214 ops[0] = eps[0], ops[1] = eps[1];
215 output->ps = ops;
216 return 0;
217 }
218
219 /* if endpoints in same triangle, use a single line */
220 if (ftrii == ltrii) {
221 LIST_FREE(&tris);
222 free(dq.pnlps);
223 free(pnlps);
224 free(pnls);
225 if (growops(2) != 0)
226 return -2;
227 output->pn = 2;
228 ops[0] = eps[0], ops[1] = eps[1];
229 output->ps = ops;
230 return 0;
231 }
232
233 /* build funnel and shortest path linked list (in add2dq) */
234 epnls[0].pp = &eps[0], epnls[0].link = NULL;
235 epnls[1].pp = &eps[1], epnls[1].link = NULL;
236 add2dq(&dq, DQ_FRONT, &epnls[0]);
237 dq.apex = dq.fpnlpi;
238 trii = ftrii;
239 while (trii != SIZE_MAX) {
240 trip = LIST_AT(&tris, trii);
241 trip->mark = 2;
242
243 /* find the left and right points of the exiting edge */
244 for (ei = 0; ei < 3; ei++)
245 if (trip->e[ei].right_index != SIZE_MAX &&
246 LIST_GET(&tris, trip->e[ei].right_index).mark == 1)
247 break;
248 if (ei == 3) { /* in last triangle */
249 if (ccw(eps[1], *dq.pnlps[dq.fpnlpi]->pp, *dq.pnlps[dq.lpnlpi]->pp) ==
250 ISCCW)
251 lpnlp = dq.pnlps[dq.lpnlpi], rpnlp = &epnls[1];
252 else
253 lpnlp = &epnls[1], rpnlp = dq.pnlps[dq.lpnlpi];
254 } else {
255 pnlp = trip->e[(ei + 1) % 3].pnl1p;
256 if (ccw(*trip->e[ei].pnl0p->pp, *pnlp->pp, *trip->e[ei].pnl1p->pp) ==
257 ISCCW)
258 lpnlp = trip->e[ei].pnl1p, rpnlp = trip->e[ei].pnl0p;
259 else
260 lpnlp = trip->e[ei].pnl0p, rpnlp = trip->e[ei].pnl1p;
261 }
262
263 /* update deque */
264 if (trii == ftrii) {
265 add2dq(&dq, DQ_BACK, lpnlp);
266 add2dq(&dq, DQ_FRONT, rpnlp);
267 } else {
268 if (dq.pnlps[dq.fpnlpi] != rpnlp && dq.pnlps[dq.lpnlpi] != rpnlp) {
269 /* add right point to deque */
270 size_t splitindex = finddqsplit(&dq, rpnlp);
271 splitdq(&dq, DQ_BACK, splitindex);
272 add2dq(&dq, DQ_FRONT, rpnlp);
273 /* if the split is behind the apex, then reset apex */
274 if (splitindex > dq.apex)
275 dq.apex = splitindex;
276 } else {
277 /* add left point to deque */
278 size_t splitindex = finddqsplit(&dq, lpnlp);
279 splitdq(&dq, DQ_FRONT, splitindex);
280 add2dq(&dq, DQ_BACK, lpnlp);
281 /* if the split is in front of the apex, then reset apex */
282 if (splitindex < dq.apex)
283 dq.apex = splitindex;
284 }
285 }
286 trii = SIZE_MAX;
287 for (ei = 0; ei < 3; ei++)
288 if (trip->e[ei].right_index != SIZE_MAX &&
289 LIST_GET(&tris, trip->e[ei].right_index).mark == 1) {
290 trii = trip->e[ei].right_index;
291 break;
292 }
293 }
294
295#if defined(DEBUG) && DEBUG >= 1
296 fprintf(stderr, "polypath");
297 for (pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
298 fprintf(stderr, " %f %f", pnlp->pp->x, pnlp->pp->y);
299 fprintf(stderr, "\n");
300#endif
301
302 LIST_FREE(&tris);
303 free(dq.pnlps);
304 size_t i;
305 for (i = 0, pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
306 i++;
307 if (growops(i) != 0) {
308 free(pnlps);
309 free(pnls);
310 return -2;
311 }
312 output->pn = i;
313 for (i = i - 1, pnlp = &epnls[1]; pnlp; i--, pnlp = pnlp->link)
314 ops[i] = *pnlp->pp;
315 output->ps = ops;
316 free(pnlps);
317 free(pnls);
318
319 return 0;
320}
321
322/* triangulate polygon */
323static int triangulate(triangles_t *tris, pointnlink_t **points,
324 size_t point_count) {
325 if (point_count > 3) {
326 for (size_t pnli = 0; pnli < point_count; pnli++) {
327 const size_t pnlip1 = (pnli + 1) % point_count;
328 const size_t pnlip2 = (pnli + 2) % point_count;
329 if (isdiagonal(pnli, points, point_count, point_indexer)) {
330 if (loadtriangle(tris, points[pnli], points[pnlip1], points[pnlip2]) !=
331 0)
332 return -1;
333 for (pnli = pnlip1; pnli < point_count - 1; pnli++)
334 points[pnli] = points[pnli + 1];
335 return triangulate(tris, points, point_count - 1);
336 }
337 }
338 prerror("triangulation failed");
339 } else {
340 if (loadtriangle(tris, points[0], points[1], points[2]) != 0)
341 return -1;
342 }
343
344 return 0;
345}
346
347static int loadtriangle(triangles_t *tris, pointnlink_t *pnlap,
348 pointnlink_t *pnlbp, pointnlink_t *pnlcp) {
349 triangle_t trip = {0};
350 trip.e[0].pnl0p = pnlap, trip.e[0].pnl1p = pnlbp,
351 trip.e[0].right_index = SIZE_MAX;
352 trip.e[1].pnl0p = pnlbp, trip.e[1].pnl1p = pnlcp,
353 trip.e[1].right_index = SIZE_MAX;
354 trip.e[2].pnl0p = pnlcp, trip.e[2].pnl1p = pnlap,
355 trip.e[2].right_index = SIZE_MAX;
356
357 if (!LIST_TRY_APPEND(tris, trip)) {
358 prerror("cannot realloc tris");
359 return -1;
360 }
361
362 return 0;
363}
364
365/* connect a pair of triangles at their common edge (if any) */
366static void connecttris(triangles_t *tris, size_t tri1, size_t tri2) {
367 triangle_t *tri1p, *tri2p;
368 int ei, ej;
369
370 for (ei = 0; ei < 3; ei++) {
371 for (ej = 0; ej < 3; ej++) {
372 tri1p = LIST_AT(tris, tri1);
373 tri2p = LIST_AT(tris, tri2);
374 if ((tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl0p->pp &&
375 tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl1p->pp) ||
376 (tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl1p->pp &&
377 tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl0p->pp))
378 tri1p->e[ei].right_index = tri2, tri2p->e[ej].right_index = tri1;
379 }
380 }
381}
382
383/* find and mark path from trii, to trij */
384static bool marktripath(triangles_t *tris, size_t trii, size_t trij) {
385 assert(tris != NULL);
386 int ei;
387
388 if (LIST_GET(tris, trii).mark)
389 return false;
390 LIST_AT(tris, trii)->mark = 1;
391 if (trii == trij)
392 return true;
393 for (ei = 0; ei < 3; ei++)
394 if (LIST_GET(tris, trii).e[ei].right_index != SIZE_MAX &&
395 marktripath(tris, LIST_GET(tris, trii).e[ei].right_index, trij))
396 return true;
397 LIST_AT(tris, trii)->mark = 0;
398 return false;
399}
400
401/* add a new point to the deque, either front or back */
402static void add2dq(deque_t *dq, int side, pointnlink_t *pnlp) {
403 if (side == DQ_FRONT) {
404 if (dq->lpnlpi >= dq->fpnlpi)
405 pnlp->link = dq->pnlps[dq->fpnlpi]; /* shortest path links */
406 dq->fpnlpi--;
407 dq->pnlps[dq->fpnlpi] = pnlp;
408 } else {
409 if (dq->lpnlpi >= dq->fpnlpi)
410 pnlp->link = dq->pnlps[dq->lpnlpi]; /* shortest path links */
411 dq->lpnlpi++;
412 dq->pnlps[dq->lpnlpi] = pnlp;
413 }
414}
415
416static void splitdq(deque_t *dq, int side, size_t index) {
417 if (side == DQ_FRONT)
418 dq->lpnlpi = index;
419 else
420 dq->fpnlpi = index;
421}
422
423static size_t finddqsplit(const deque_t *dq, pointnlink_t *pnlp) {
424 for (size_t index = dq->fpnlpi; index < dq->apex; index++)
425 if (ccw(*dq->pnlps[index + 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) ==
426 ISCCW)
427 return index;
428 for (size_t index = dq->lpnlpi; index > dq->apex; index--)
429 if (ccw(*dq->pnlps[index - 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) ==
430 ISCW)
431 return index;
432 return dq->apex;
433}
434
435static int pointintri(const triangles_t tris, size_t trii, Ppoint_t *pp) {
436 int ei, sum;
437
438 for (ei = 0, sum = 0; ei < 3; ei++)
439 if (ccw(*LIST_GET(&tris, trii).e[ei].pnl0p->pp,
440 *LIST_GET(&tris, trii).e[ei].pnl1p->pp, *pp) != ISCW)
441 sum++;
442 return sum == 3 || sum == 0;
443}
444
445static int growops(size_t newopn) {
446 if (newopn <= opn)
447 return 0;
448 Ppoint_t *new_ops = realloc(ops, POINTSIZE * newopn);
449 if (new_ops == NULL) {
450 prerror("cannot realloc ops");
451 return -1;
452 }
453 ops = new_ops;
454 opn = newopn;
455
456 return 0;
457}
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:181
static gdPoint * points
static void mark(const stk_t *stk, Agnode_t *n)
set a mark on n
Definition ccomps.c:38
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:210
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_FREE(list)
Definition list.h:413
#define LIST_GET(list, index)
Definition list.h:197
#define LIST_TRY_APPEND(list, item)
Definition list.h:118
#define PRISIZE_T
Definition prisize_t.h:25
static size_t opn
Definition route.c:36
static Ppoint_t * ops
Definition route.c:35
static size_t finddqsplit(const deque_t *dq, pointnlink_t *pnlp)
Definition shortest.c:423
#define DQ_FRONT
Definition shortest.c:23
#define POINTSIZE
Definition shortest.c:29
static void splitdq(deque_t *dq, int side, size_t index)
Definition shortest.c:416
static void add2dq(deque_t *dq, int side, pointnlink_t *pnlp)
Definition shortest.c:402
static int pointintri(const triangles_t tris, size_t trii, Ppoint_t *pp)
Definition shortest.c:435
int Pshortestpath(Ppoly_t *polyp, Ppoint_t eps[2], Ppolyline_t *output)
Definition shortest.c:84
static bool marktripath(triangles_t *tris, size_t trii, size_t trij)
Definition shortest.c:384
#define DQ_BACK
Definition shortest.c:24
static int loadtriangle(triangles_t *tris, pointnlink_t *pnlap, pointnlink_t *pnlbp, pointnlink_t *pnlcp)
Definition shortest.c:347
#define POINTNLINKPSIZE
Definition shortest.c:37
static int triangulate(triangles_t *tris, pointnlink_t **points, size_t point_count)
Definition shortest.c:323
#define prerror(msg)
Definition shortest.c:26
static int growops(size_t newopn)
Definition shortest.c:445
static void connecttris(triangles_t *tris, size_t tri1, size_t tri2)
Definition shortest.c:366
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
size_t lpnlpi
Definition shortest.c:52
size_t pnlpn
Definition shortest.c:52
size_t fpnlpi
Definition shortest.c:52
size_t apex
Definition shortest.c:52
pointnlink_t ** pnlps
Definition shortest.c:51
pointnlink_t * pnl0p
Definition shortest.c:40
size_t right_index
index into tris of the triangle to the right
Definition shortest.c:42
pointnlink_t * pnl1p
Definition shortest.c:41
tedge_t e[3]
Definition shortest.c:47
@ ISCW
clockwise
Definition tri.h:42
@ ISCCW
counter-clockwise
Definition tri.h:41
int ccw(Ppoint_t p1, Ppoint_t p2, Ppoint_t p3)
are the given points counter-clockwise, clockwise, or co-linear?
Definition triang.c:25
bool isdiagonal(size_t i, void *pointp, size_t pointn, indexer_t indexer)
is (i, i + 2) a diagonal?
Definition triang.c:115
static Ppoint_t point_indexer(void *base, size_t index)
Definition triang.c:30