Graphviz 14.1.3~dev.20260124.0732
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 "config.h"
12
13#include <stdbool.h>
14#include <stdio.h>
15#include <stdint.h>
16#include <stdlib.h>
17#include <math.h>
18#include <pathplan/pathutil.h>
19#include <pathplan/tri.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
55static LIST(triangle_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 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 LIST_CLEAR(&tris);
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
130 && polyp->ps[pi].x == polyp->ps[pi + 1].x
131 && polyp->ps[pi].y == polyp->ps[pi + 1].y)
132 continue;
133 pnls[pnll].pp = &polyp->ps[pi];
134 pnls[pnll].link = &pnls[pnll % polyp->pn];
135 pnlps[pnll] = &pnls[pnll];
136 pnll++;
137 }
138 } else {
139 for (pi = 0; pi < polyp->pn; pi++) {
140 if (pi > 0 && polyp->ps[pi].x == polyp->ps[pi - 1].x &&
141 polyp->ps[pi].y == polyp->ps[pi - 1].y)
142 continue;
143 pnls[pnll].pp = &polyp->ps[pi];
144 pnls[pnll].link = &pnls[pnll % polyp->pn];
145 pnlps[pnll] = &pnls[pnll];
146 pnll++;
147 }
148 }
149
150#if defined(DEBUG) && DEBUG >= 1
151 fprintf(stderr, "points\n%" PRISIZE_T "\n", pnll);
152 for (size_t pnli = 0; pnli < pnll; pnli++)
153 fprintf(stderr, "%f %f\n", pnls[pnli].pp->x, pnls[pnli].pp->y);
154#endif
155
156 /* generate list of triangles */
157 if (triangulate(pnlps, pnll)) {
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(trii, trij);
176
177 /* find first and last triangles */
178 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
179 if (pointintri(trii, &eps[0]))
180 break;
181 if (trii == LIST_SIZE(&tris)) {
182 prerror("source point not in any triangle");
183 free(dq.pnlps);
184 free(pnlps);
185 free(pnls);
186 return -1;
187 }
188 ftrii = trii;
189 for (trii = 0; trii < LIST_SIZE(&tris); trii++)
190 if (pointintri(trii, &eps[1]))
191 break;
192 if (trii == LIST_SIZE(&tris)) {
193 prerror("destination point not in any triangle");
194 free(dq.pnlps);
195 free(pnlps);
196 free(pnls);
197 return -1;
198 }
199 ltrii = trii;
200
201 /* mark the strip of triangles from eps[0] to eps[1] */
202 if (!marktripath(ftrii, ltrii)) {
203 prerror("cannot find triangle path");
204 free(dq.pnlps);
205 free(pnlps);
206 free(pnls);
207 /* a straight line is better than failing */
208 if (growops(2) != 0)
209 return -2;
210 output->pn = 2;
211 ops[0] = eps[0], ops[1] = eps[1];
212 output->ps = ops;
213 return 0;
214 }
215
216 /* if endpoints in same triangle, use a single line */
217 if (ftrii == ltrii) {
218 free(dq.pnlps);
219 free(pnlps);
220 free(pnls);
221 if (growops(2) != 0)
222 return -2;
223 output->pn = 2;
224 ops[0] = eps[0], ops[1] = eps[1];
225 output->ps = ops;
226 return 0;
227 }
228
229 /* build funnel and shortest path linked list (in add2dq) */
230 epnls[0].pp = &eps[0], epnls[0].link = NULL;
231 epnls[1].pp = &eps[1], epnls[1].link = NULL;
232 add2dq(&dq, DQ_FRONT, &epnls[0]);
233 dq.apex = dq.fpnlpi;
234 trii = ftrii;
235 while (trii != SIZE_MAX) {
236 trip = LIST_AT(&tris, trii);
237 trip->mark = 2;
238
239 /* find the left and right points of the exiting edge */
240 for (ei = 0; ei < 3; ei++)
241 if (trip->e[ei].right_index != SIZE_MAX && LIST_GET(&tris, trip->e[ei].right_index).mark == 1)
242 break;
243 if (ei == 3) { /* in last triangle */
244 if (ccw(eps[1], *dq.pnlps[dq.fpnlpi]->pp,
245 *dq.pnlps[dq.lpnlpi]->pp) == ISCCW)
246 lpnlp = dq.pnlps[dq.lpnlpi], rpnlp = &epnls[1];
247 else
248 lpnlp = &epnls[1], rpnlp = dq.pnlps[dq.lpnlpi];
249 } else {
250 pnlp = trip->e[(ei + 1) % 3].pnl1p;
251 if (ccw(*trip->e[ei].pnl0p->pp, *pnlp->pp,
252 *trip->e[ei].pnl1p->pp) == ISCCW)
253 lpnlp = trip->e[ei].pnl1p, rpnlp = trip->e[ei].pnl0p;
254 else
255 lpnlp = trip->e[ei].pnl0p, rpnlp = trip->e[ei].pnl1p;
256 }
257
258 /* update deque */
259 if (trii == ftrii) {
260 add2dq(&dq, DQ_BACK, lpnlp);
261 add2dq(&dq, DQ_FRONT, rpnlp);
262 } else {
263 if (dq.pnlps[dq.fpnlpi] != rpnlp
264 && dq.pnlps[dq.lpnlpi] != rpnlp) {
265 /* add right point to deque */
266 size_t splitindex = finddqsplit(&dq, rpnlp);
267 splitdq(&dq, DQ_BACK, splitindex);
268 add2dq(&dq, DQ_FRONT, rpnlp);
269 /* if the split is behind the apex, then reset apex */
270 if (splitindex > dq.apex)
271 dq.apex = splitindex;
272 } else {
273 /* add left point to deque */
274 size_t splitindex = finddqsplit(&dq, lpnlp);
275 splitdq(&dq, DQ_FRONT, splitindex);
276 add2dq(&dq, DQ_BACK, lpnlp);
277 /* if the split is in front of the apex, then reset apex */
278 if (splitindex < dq.apex)
279 dq.apex = splitindex;
280 }
281 }
282 trii = SIZE_MAX;
283 for (ei = 0; ei < 3; ei++)
284 if (trip->e[ei].right_index != SIZE_MAX && LIST_GET(&tris, trip->e[ei].right_index).mark == 1) {
285 trii = trip->e[ei].right_index;
286 break;
287 }
288 }
289
290#if defined(DEBUG) && DEBUG >= 1
291 fprintf(stderr, "polypath");
292 for (pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
293 fprintf(stderr, " %f %f", pnlp->pp->x, pnlp->pp->y);
294 fprintf(stderr, "\n");
295#endif
296
297 free(dq.pnlps);
298 size_t i;
299 for (i = 0, pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
300 i++;
301 if (growops(i) != 0) {
302 free(pnlps);
303 free(pnls);
304 return -2;
305 }
306 output->pn = i;
307 for (i = i - 1, pnlp = &epnls[1]; pnlp; i--, pnlp = pnlp->link)
308 ops[i] = *pnlp->pp;
309 output->ps = ops;
310 free(pnlps);
311 free(pnls);
312
313 return 0;
314}
315
316/* triangulate polygon */
317static int triangulate(pointnlink_t **points, size_t point_count) {
318 if (point_count > 3)
319 {
320 for (size_t pnli = 0; pnli < point_count; pnli++)
321 {
322 const size_t pnlip1 = (pnli + 1) % point_count;
323 const size_t pnlip2 = (pnli + 2) % point_count;
324 if (isdiagonal(pnli, pnlip2, points, point_count, point_indexer))
325 {
326 if (loadtriangle(points[pnli], points[pnlip1], points[pnlip2]) != 0)
327 return -1;
328 for (pnli = pnlip1; pnli < point_count - 1; pnli++)
329 points[pnli] = points[pnli + 1];
330 return triangulate(points, point_count - 1);
331 }
332 }
333 prerror("triangulation failed");
334 }
335 else {
336 if (loadtriangle(points[0], points[1], points[2]) != 0)
337 return -1;
338 }
339
340 return 0;
341}
342
343static int loadtriangle(pointnlink_t * pnlap, pointnlink_t * pnlbp,
344 pointnlink_t * pnlcp)
345{
346 triangle_t trip = {0};
347 trip.e[0].pnl0p = pnlap, trip.e[0].pnl1p = pnlbp, trip.e[0].right_index = SIZE_MAX;
348 trip.e[1].pnl0p = pnlbp, trip.e[1].pnl1p = pnlcp, trip.e[1].right_index = SIZE_MAX;
349 trip.e[2].pnl0p = pnlcp, trip.e[2].pnl1p = pnlap, trip.e[2].right_index = SIZE_MAX;
350
351 if (!LIST_TRY_APPEND(&tris, trip)) {
352 prerror("cannot realloc tris");
353 return -1;
354 }
355
356 return 0;
357}
358
359/* connect a pair of triangles at their common edge (if any) */
360static void connecttris(size_t tri1, size_t tri2) {
361 triangle_t *tri1p, *tri2p;
362 int ei, ej;
363
364 for (ei = 0; ei < 3; ei++) {
365 for (ej = 0; ej < 3; ej++) {
366 tri1p = LIST_AT(&tris, tri1);
367 tri2p = LIST_AT(&tris, tri2);
368 if ((tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl0p->pp &&
369 tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl1p->pp) ||
370 (tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl1p->pp &&
371 tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl0p->pp))
372 tri1p->e[ei].right_index = tri2, tri2p->e[ej].right_index = tri1;
373 }
374 }
375}
376
377/* find and mark path from trii, to trij */
378static bool marktripath(size_t trii, size_t trij) {
379 int ei;
380
381 if (LIST_GET(&tris, trii).mark)
382 return false;
383 LIST_AT(&tris, trii)->mark = 1;
384 if (trii == trij)
385 return true;
386 for (ei = 0; ei < 3; ei++)
387 if (LIST_GET(&tris, trii).e[ei].right_index != SIZE_MAX &&
388 marktripath(LIST_GET(&tris, trii).e[ei].right_index, trij))
389 return true;
390 LIST_AT(&tris, trii)->mark = 0;
391 return false;
392}
393
394/* add a new point to the deque, either front or back */
395static void add2dq(deque_t *dq, int side, pointnlink_t *pnlp) {
396 if (side == DQ_FRONT) {
397 if (dq->lpnlpi >= dq->fpnlpi)
398 pnlp->link = dq->pnlps[dq->fpnlpi]; /* shortest path links */
399 dq->fpnlpi--;
400 dq->pnlps[dq->fpnlpi] = pnlp;
401 } else {
402 if (dq->lpnlpi >= dq->fpnlpi)
403 pnlp->link = dq->pnlps[dq->lpnlpi]; /* shortest path links */
404 dq->lpnlpi++;
405 dq->pnlps[dq->lpnlpi] = pnlp;
406 }
407}
408
409static void splitdq(deque_t *dq, int side, size_t index) {
410 if (side == DQ_FRONT)
411 dq->lpnlpi = index;
412 else
413 dq->fpnlpi = index;
414}
415
416static size_t finddqsplit(const deque_t *dq, pointnlink_t *pnlp) {
417 for (size_t index = dq->fpnlpi; index < dq->apex; index++)
418 if (ccw(*dq->pnlps[index + 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) == ISCCW)
419 return index;
420 for (size_t index = dq->lpnlpi; index > dq->apex; index--)
421 if (ccw(*dq->pnlps[index - 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) == ISCW)
422 return index;
423 return dq->apex;
424}
425
426static int pointintri(size_t trii, Ppoint_t *pp) {
427 int ei, sum;
428
429 for (ei = 0, sum = 0; ei < 3; ei++)
430 if (ccw(*LIST_GET(&tris, trii).e[ei].pnl0p->pp,
431 *LIST_GET(&tris, trii).e[ei].pnl1p->pp, *pp) != ISCW)
432 sum++;
433 return sum == 3 || sum == 0;
434}
435
436static int growops(size_t newopn) {
437 if (newopn <= opn)
438 return 0;
439 Ppoint_t *new_ops = realloc(ops, POINTSIZE * newopn);
440 if (new_ops == NULL) {
441 prerror("cannot realloc ops");
442 return -1;
443 }
444 ops = new_ops;
445 opn = newopn;
446
447 return 0;
448}
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: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:36
static Ppoint_t * ops
Definition route.c:35
static size_t finddqsplit(const deque_t *dq, pointnlink_t *pnlp)
Definition shortest.c:416
static int triangulate(pointnlink_t **points, size_t point_count)
Definition shortest.c:317
static bool marktripath(size_t trii, size_t trij)
Definition shortest.c:378
#define DQ_FRONT
Definition shortest.c:23
#define POINTSIZE
Definition shortest.c:29
static void connecttris(size_t tri1, size_t tri2)
Definition shortest.c:360
static int pointintri(size_t trii, Ppoint_t *pp)
Definition shortest.c:426
static void splitdq(deque_t *dq, int side, size_t index)
Definition shortest.c:409
static void add2dq(deque_t *dq, int side, pointnlink_t *pnlp)
Definition shortest.c:395
int Pshortestpath(Ppoly_t *polyp, Ppoint_t eps[2], Ppolyline_t *output)
Definition shortest.c:83
#define DQ_BACK
Definition shortest.c:24
#define POINTNLINKPSIZE
Definition shortest.c:37
#define prerror(msg)
Definition shortest.c:26
static int growops(size_t newopn)
Definition shortest.c:436
static int loadtriangle(pointnlink_t *pnlap, pointnlink_t *pnlbp, pointnlink_t *pnlcp)
Definition shortest.c:343
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
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:122
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
@ 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:30