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