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