123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include <cgraph/list.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <math.h>
- #include <pathplan/pathutil.h>
- #include <pathplan/tri.h>
- #include <util/prisize_t.h>
- #define DQ_FRONT 1
- #define DQ_BACK 2
- #define prerror(msg) \
- fprintf (stderr, "lib/pathplan/%s:%d: %s\n", __FILE__, __LINE__, (msg))
- #define POINTSIZE sizeof (Ppoint_t)
- typedef struct pointnlink_t {
- Ppoint_t *pp;
- struct pointnlink_t *link;
- } pointnlink_t;
- #define POINTNLINKSIZE sizeof (pointnlink_t)
- #define POINTNLINKPSIZE sizeof (pointnlink_t *)
- typedef struct {
- pointnlink_t *pnl0p;
- pointnlink_t *pnl1p;
- size_t right_index; ///< index into \p tris of the triangle to the right
- } tedge_t;
- typedef struct triangle_t {
- int mark;
- tedge_t e[3];
- } triangle_t;
- DEFINE_LIST(triangles, triangle_t)
- typedef struct deque_t {
- pointnlink_t **pnlps;
- size_t pnlpn, fpnlpi, lpnlpi, apex;
- } deque_t;
- static triangles_t tris;
- static Ppoint_t *ops;
- static size_t opn;
- static int triangulate(pointnlink_t **, size_t);
- static int loadtriangle(pointnlink_t *, pointnlink_t *, pointnlink_t *);
- static void connecttris(size_t, size_t);
- static bool marktripath(size_t, size_t);
- static void add2dq(deque_t *dq, int, pointnlink_t*);
- static void splitdq(deque_t *dq, int, size_t);
- static size_t finddqsplit(const deque_t *dq, pointnlink_t*);
- static int pointintri(size_t, Ppoint_t *);
- static int growops(size_t);
- static Ppoint_t point_indexer(void *base, size_t index) {
- pointnlink_t **b = base;
- return *b[index]->pp;
- }
- /* Pshortestpath:
- * Find a shortest path contained in the polygon polyp going between the
- * points supplied in eps. The resulting polyline is stored in output.
- * Return 0 on success, -1 on bad input, -2 on memory allocation problem.
- */
- int Pshortestpath(Ppoly_t * polyp, Ppoint_t eps[2], Ppolyline_t * output)
- {
- size_t pi, minpi;
- double minx;
- Ppoint_t p1, p2, p3;
- size_t trii, trij, ftrii, ltrii;
- int ei;
- pointnlink_t epnls[2], *lpnlp, *rpnlp, *pnlp;
- triangle_t *trip;
- /* make space */
- pointnlink_t *pnls = calloc(polyp->pn, sizeof(pnls[0]));
- if (polyp->pn > 0 && pnls == NULL) {
- prerror("cannot realloc pnls");
- return -2;
- }
- pointnlink_t **pnlps = calloc(polyp->pn, sizeof(pnlps[0]));
- if (polyp->pn > 0 && pnlps == NULL) {
- prerror("cannot realloc pnlps");
- free(pnls);
- return -2;
- }
- size_t pnll = 0;
- triangles_clear(&tris);
- deque_t dq = {.pnlpn = polyp->pn * 2};
- dq.pnlps = calloc(dq.pnlpn, POINTNLINKPSIZE);
- if (dq.pnlps == NULL) {
- prerror("cannot realloc dq.pnls");
- free(pnlps);
- free(pnls);
- return -2;
- }
- dq.fpnlpi = dq.pnlpn / 2;
- dq.lpnlpi = dq.fpnlpi - 1;
- /* make sure polygon is CCW and load pnls array */
- for (pi = 0, minx = HUGE_VAL, minpi = SIZE_MAX; pi < polyp->pn; pi++) {
- if (minx > polyp->ps[pi].x)
- minx = polyp->ps[pi].x, minpi = pi;
- }
- p2 = polyp->ps[minpi];
- p1 = polyp->ps[minpi == 0 ? polyp->pn - 1 : minpi - 1];
- p3 = polyp->ps[(minpi == polyp->pn - 1) ? 0 : minpi + 1];
- if ((p1.x == p2.x && p2.x == p3.x && p3.y > p2.y) ||
- ccw(p1, p2, p3) != ISCCW) {
- for (pi = polyp->pn - 1; polyp->pn > 0 && pi != SIZE_MAX; pi--) {
- if (pi < polyp->pn - 1
- && polyp->ps[pi].x == polyp->ps[pi + 1].x
- && polyp->ps[pi].y == polyp->ps[pi + 1].y)
- continue;
- pnls[pnll].pp = &polyp->ps[pi];
- pnls[pnll].link = &pnls[pnll % polyp->pn];
- pnlps[pnll] = &pnls[pnll];
- pnll++;
- }
- } else {
- for (pi = 0; pi < polyp->pn; pi++) {
- if (pi > 0 && polyp->ps[pi].x == polyp->ps[pi - 1].x &&
- polyp->ps[pi].y == polyp->ps[pi - 1].y)
- continue;
- pnls[pnll].pp = &polyp->ps[pi];
- pnls[pnll].link = &pnls[pnll % polyp->pn];
- pnlps[pnll] = &pnls[pnll];
- pnll++;
- }
- }
- #if defined(DEBUG) && DEBUG >= 1
- fprintf(stderr, "points\n%" PRISIZE_T "\n", pnll);
- for (size_t pnli = 0; pnli < pnll; pnli++)
- fprintf(stderr, "%f %f\n", pnls[pnli].pp->x, pnls[pnli].pp->y);
- #endif
- /* generate list of triangles */
- if (triangulate(pnlps, pnll)) {
- free(dq.pnlps);
- free(pnlps);
- free(pnls);
- return -2;
- }
- #if defined(DEBUG) && DEBUG >= 2
- fprintf(stderr, "triangles\n%" PRISIZE_T "\n", triangles_size(&tris));
- for (trii = 0; trii < triangles_size(&tris); trii++)
- for (ei = 0; ei < 3; ei++)
- fprintf(stderr, "%f %f\n", triangles_get(&tris, trii).e[ei].pnl0p->pp->x,
- triangles_get(&tris, trii).e[ei].pnl0p->pp->y);
- #endif
- /* connect all pairs of triangles that share an edge */
- for (trii = 0; trii < triangles_size(&tris); trii++)
- for (trij = trii + 1; trij < triangles_size(&tris); trij++)
- connecttris(trii, trij);
- /* find first and last triangles */
- for (trii = 0; trii < triangles_size(&tris); trii++)
- if (pointintri(trii, &eps[0]))
- break;
- if (trii == triangles_size(&tris)) {
- prerror("source point not in any triangle");
- free(dq.pnlps);
- free(pnlps);
- free(pnls);
- return -1;
- }
- ftrii = trii;
- for (trii = 0; trii < triangles_size(&tris); trii++)
- if (pointintri(trii, &eps[1]))
- break;
- if (trii == triangles_size(&tris)) {
- prerror("destination point not in any triangle");
- free(dq.pnlps);
- free(pnlps);
- free(pnls);
- return -1;
- }
- ltrii = trii;
- /* mark the strip of triangles from eps[0] to eps[1] */
- if (!marktripath(ftrii, ltrii)) {
- prerror("cannot find triangle path");
- free(dq.pnlps);
- free(pnlps);
- free(pnls);
- /* a straight line is better than failing */
- if (growops(2) != 0)
- return -2;
- output->pn = 2;
- ops[0] = eps[0], ops[1] = eps[1];
- output->ps = ops;
- return 0;
- }
- /* if endpoints in same triangle, use a single line */
- if (ftrii == ltrii) {
- free(dq.pnlps);
- free(pnlps);
- free(pnls);
- if (growops(2) != 0)
- return -2;
- output->pn = 2;
- ops[0] = eps[0], ops[1] = eps[1];
- output->ps = ops;
- return 0;
- }
- /* build funnel and shortest path linked list (in add2dq) */
- epnls[0].pp = &eps[0], epnls[0].link = NULL;
- epnls[1].pp = &eps[1], epnls[1].link = NULL;
- add2dq(&dq, DQ_FRONT, &epnls[0]);
- dq.apex = dq.fpnlpi;
- trii = ftrii;
- while (trii != SIZE_MAX) {
- trip = triangles_at(&tris, trii);
- trip->mark = 2;
- /* find the left and right points of the exiting edge */
- for (ei = 0; ei < 3; ei++)
- if (trip->e[ei].right_index != SIZE_MAX && triangles_get(&tris, trip->e[ei].right_index).mark == 1)
- break;
- if (ei == 3) { /* in last triangle */
- if (ccw(eps[1], *dq.pnlps[dq.fpnlpi]->pp,
- *dq.pnlps[dq.lpnlpi]->pp) == ISCCW)
- lpnlp = dq.pnlps[dq.lpnlpi], rpnlp = &epnls[1];
- else
- lpnlp = &epnls[1], rpnlp = dq.pnlps[dq.lpnlpi];
- } else {
- pnlp = trip->e[(ei + 1) % 3].pnl1p;
- if (ccw(*trip->e[ei].pnl0p->pp, *pnlp->pp,
- *trip->e[ei].pnl1p->pp) == ISCCW)
- lpnlp = trip->e[ei].pnl1p, rpnlp = trip->e[ei].pnl0p;
- else
- lpnlp = trip->e[ei].pnl0p, rpnlp = trip->e[ei].pnl1p;
- }
- /* update deque */
- if (trii == ftrii) {
- add2dq(&dq, DQ_BACK, lpnlp);
- add2dq(&dq, DQ_FRONT, rpnlp);
- } else {
- if (dq.pnlps[dq.fpnlpi] != rpnlp
- && dq.pnlps[dq.lpnlpi] != rpnlp) {
- /* add right point to deque */
- size_t splitindex = finddqsplit(&dq, rpnlp);
- splitdq(&dq, DQ_BACK, splitindex);
- add2dq(&dq, DQ_FRONT, rpnlp);
- /* if the split is behind the apex, then reset apex */
- if (splitindex > dq.apex)
- dq.apex = splitindex;
- } else {
- /* add left point to deque */
- size_t splitindex = finddqsplit(&dq, lpnlp);
- splitdq(&dq, DQ_FRONT, splitindex);
- add2dq(&dq, DQ_BACK, lpnlp);
- /* if the split is in front of the apex, then reset apex */
- if (splitindex < dq.apex)
- dq.apex = splitindex;
- }
- }
- trii = SIZE_MAX;
- for (ei = 0; ei < 3; ei++)
- if (trip->e[ei].right_index != SIZE_MAX && triangles_get(&tris, trip->e[ei].right_index).mark == 1) {
- trii = trip->e[ei].right_index;
- break;
- }
- }
- #if defined(DEBUG) && DEBUG >= 1
- fprintf(stderr, "polypath");
- for (pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
- fprintf(stderr, " %f %f", pnlp->pp->x, pnlp->pp->y);
- fprintf(stderr, "\n");
- #endif
- free(dq.pnlps);
- size_t i;
- for (i = 0, pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
- i++;
- if (growops(i) != 0) {
- free(pnlps);
- free(pnls);
- return -2;
- }
- output->pn = i;
- for (i = i - 1, pnlp = &epnls[1]; pnlp; i--, pnlp = pnlp->link)
- ops[i] = *pnlp->pp;
- output->ps = ops;
- free(pnlps);
- free(pnls);
- return 0;
- }
- /* triangulate polygon */
- static int triangulate(pointnlink_t **points, size_t point_count) {
- if (point_count > 3)
- {
- for (size_t pnli = 0; pnli < point_count; pnli++)
- {
- const size_t pnlip1 = (pnli + 1) % point_count;
- const size_t pnlip2 = (pnli + 2) % point_count;
- if (isdiagonal(pnli, pnlip2, points, point_count, point_indexer))
- {
- if (loadtriangle(points[pnli], points[pnlip1], points[pnlip2]) != 0)
- return -1;
- for (pnli = pnlip1; pnli < point_count - 1; pnli++)
- points[pnli] = points[pnli + 1];
- return triangulate(points, point_count - 1);
- }
- }
- prerror("triangulation failed");
- }
- else {
- if (loadtriangle(points[0], points[1], points[2]) != 0)
- return -1;
- }
- return 0;
- }
- static int loadtriangle(pointnlink_t * pnlap, pointnlink_t * pnlbp,
- pointnlink_t * pnlcp)
- {
- triangle_t trip = {0};
- trip.e[0].pnl0p = pnlap, trip.e[0].pnl1p = pnlbp, trip.e[0].right_index = SIZE_MAX;
- trip.e[1].pnl0p = pnlbp, trip.e[1].pnl1p = pnlcp, trip.e[1].right_index = SIZE_MAX;
- trip.e[2].pnl0p = pnlcp, trip.e[2].pnl1p = pnlap, trip.e[2].right_index = SIZE_MAX;
- if (triangles_try_append(&tris, trip) != 0) {
- prerror("cannot realloc tris");
- return -1;
- }
- return 0;
- }
- /* connect a pair of triangles at their common edge (if any) */
- static void connecttris(size_t tri1, size_t tri2) {
- triangle_t *tri1p, *tri2p;
- int ei, ej;
- for (ei = 0; ei < 3; ei++) {
- for (ej = 0; ej < 3; ej++) {
- tri1p = triangles_at(&tris, tri1);
- tri2p = triangles_at(&tris, tri2);
- if ((tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl0p->pp &&
- tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl1p->pp) ||
- (tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl1p->pp &&
- tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl0p->pp))
- tri1p->e[ei].right_index = tri2, tri2p->e[ej].right_index = tri1;
- }
- }
- }
- /* find and mark path from trii, to trij */
- static bool marktripath(size_t trii, size_t trij) {
- int ei;
- if (triangles_get(&tris, trii).mark)
- return false;
- triangles_at(&tris, trii)->mark = 1;
- if (trii == trij)
- return true;
- for (ei = 0; ei < 3; ei++)
- if (triangles_get(&tris, trii).e[ei].right_index != SIZE_MAX &&
- marktripath(triangles_get(&tris, trii).e[ei].right_index, trij))
- return true;
- triangles_at(&tris, trii)->mark = 0;
- return false;
- }
- /* add a new point to the deque, either front or back */
- static void add2dq(deque_t *dq, int side, pointnlink_t *pnlp) {
- if (side == DQ_FRONT) {
- if (dq->lpnlpi >= dq->fpnlpi)
- pnlp->link = dq->pnlps[dq->fpnlpi]; /* shortest path links */
- dq->fpnlpi--;
- dq->pnlps[dq->fpnlpi] = pnlp;
- } else {
- if (dq->lpnlpi >= dq->fpnlpi)
- pnlp->link = dq->pnlps[dq->lpnlpi]; /* shortest path links */
- dq->lpnlpi++;
- dq->pnlps[dq->lpnlpi] = pnlp;
- }
- }
- static void splitdq(deque_t *dq, int side, size_t index) {
- if (side == DQ_FRONT)
- dq->lpnlpi = index;
- else
- dq->fpnlpi = index;
- }
- static size_t finddqsplit(const deque_t *dq, pointnlink_t *pnlp) {
- for (size_t index = dq->fpnlpi; index < dq->apex; index++)
- if (ccw(*dq->pnlps[index + 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) == ISCCW)
- return index;
- for (size_t index = dq->lpnlpi; index > dq->apex; index--)
- if (ccw(*dq->pnlps[index - 1]->pp, *dq->pnlps[index]->pp, *pnlp->pp) == ISCW)
- return index;
- return dq->apex;
- }
- static int pointintri(size_t trii, Ppoint_t *pp) {
- int ei, sum;
- for (ei = 0, sum = 0; ei < 3; ei++)
- if (ccw(*triangles_get(&tris, trii).e[ei].pnl0p->pp,
- *triangles_get(&tris, trii).e[ei].pnl1p->pp, *pp) != ISCW)
- sum++;
- return sum == 3 || sum == 0;
- }
- static int growops(size_t newopn) {
- if (newopn <= opn)
- return 0;
- Ppoint_t *new_ops = realloc(ops, POINTSIZE * newopn);
- if (new_ops == NULL) {
- prerror("cannot realloc ops");
- return -1;
- }
- ops = new_ops;
- opn = newopn;
- return 0;
- }
|