Graphviz  2.31.20130617.0446
lib/pathplan/shortest.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include <stdlib.h>
00016 #include <stdio.h>
00017 #include <setjmp.h>
00018 #ifdef HAVE_MALLOC_H
00019 #include <malloc.h>
00020 #endif
00021 #include <limits.h>
00022 #include <math.h>
00023 #include "pathutil.h"
00024 
00025 #ifdef DMALLOC
00026 #include "dmalloc.h"
00027 #endif
00028 
00029 #define ISCCW 1
00030 #define ISCW  2
00031 #define ISON  3
00032 
00033 #define DQ_FRONT 1
00034 #define DQ_BACK  2
00035 
00036 #ifndef TRUE
00037 #define TRUE 1
00038 #define FALSE 0
00039 #endif
00040 
00041 #define prerror(msg) \
00042         fprintf (stderr, "libpath/%s:%d: %s\n", __FILE__, __LINE__, (msg))
00043 
00044 #define POINTSIZE sizeof (Ppoint_t)
00045 
00046 typedef struct pointnlink_t {
00047     Ppoint_t *pp;
00048     struct pointnlink_t *link;
00049 } pointnlink_t;
00050 
00051 #define POINTNLINKSIZE sizeof (pointnlink_t)
00052 #define POINTNLINKPSIZE sizeof (pointnlink_t *)
00053 
00054 typedef struct tedge_t {
00055     pointnlink_t *pnl0p;
00056     pointnlink_t *pnl1p;
00057     struct triangle_t *ltp;
00058     struct triangle_t *rtp;
00059 } tedge_t;
00060 
00061 typedef struct triangle_t {
00062     int mark;
00063     struct tedge_t e[3];
00064 } triangle_t;
00065 
00066 #define TRIANGLESIZE sizeof (triangle_t)
00067 
00068 typedef struct deque_t {
00069     pointnlink_t **pnlps;
00070     int pnlpn, fpnlpi, lpnlpi, apex;
00071 } deque_t;
00072 
00073 static jmp_buf jbuf;
00074 static pointnlink_t *pnls, **pnlps;
00075 static int pnln, pnll;
00076 
00077 static triangle_t *tris;
00078 static int trin, tril;
00079 
00080 static deque_t dq;
00081 
00082 static Ppoint_t *ops;
00083 static int opn;
00084 
00085 static void triangulate(pointnlink_t **, int);
00086 static int isdiagonal(int, int, pointnlink_t **, int);
00087 static void loadtriangle(pointnlink_t *, pointnlink_t *, pointnlink_t *);
00088 static void connecttris(int, int);
00089 static int marktripath(int, int);
00090 
00091 static void add2dq(int, pointnlink_t *);
00092 static void splitdq(int, int);
00093 static int finddqsplit(pointnlink_t *);
00094 
00095 static int ccw(Ppoint_t *, Ppoint_t *, Ppoint_t *);
00096 static int intersects(Ppoint_t *, Ppoint_t *, Ppoint_t *, Ppoint_t *);
00097 static int between(Ppoint_t *, Ppoint_t *, Ppoint_t *);
00098 static int pointintri(int, Ppoint_t *);
00099 
00100 static void growpnls(int);
00101 static void growtris(int);
00102 static void growdq(int);
00103 static void growops(int);
00104 
00105 /* Pshortestpath:
00106  * Find a shortest path contained in the polygon polyp going between the
00107  * points supplied in eps. The resulting polyline is stored in output.
00108  * Return 0 on success, -1 on bad input, -2 on memory allocation problem. 
00109  */
00110 int Pshortestpath(Ppoly_t * polyp, Ppoint_t * eps, Ppolyline_t * output)
00111 {
00112     int pi, minpi;
00113     double minx;
00114     Ppoint_t p1, p2, p3;
00115     int trii, trij, ftrii, ltrii;
00116     int ei;
00117     pointnlink_t epnls[2], *lpnlp, *rpnlp, *pnlp;
00118     triangle_t *trip;
00119     int splitindex;
00120 #ifdef DEBUG
00121     int pnli;
00122 #endif
00123 
00124     if (setjmp(jbuf))
00125         return -2;
00126     /* make space */
00127     growpnls(polyp->pn);
00128     pnll = 0;
00129     tril = 0;
00130     growdq(polyp->pn * 2);
00131     dq.fpnlpi = dq.pnlpn / 2, dq.lpnlpi = dq.fpnlpi - 1;
00132 
00133     /* make sure polygon is CCW and load pnls array */
00134     for (pi = 0, minx = HUGE_VAL, minpi = -1; pi < polyp->pn; pi++) {
00135         if (minx > polyp->ps[pi].x)
00136             minx = polyp->ps[pi].x, minpi = pi;
00137     }
00138     p2 = polyp->ps[minpi];
00139     p1 = polyp->ps[((minpi == 0) ? polyp->pn - 1 : minpi - 1)];
00140     p3 = polyp->ps[((minpi == polyp->pn - 1) ? 0 : minpi + 1)];
00141     if (((p1.x == p2.x && p2.x == p3.x) && (p3.y > p2.y)) ||
00142         ccw(&p1, &p2, &p3) != ISCCW) {
00143         for (pi = polyp->pn - 1; pi >= 0; pi--) {
00144             if (pi < polyp->pn - 1
00145                 && polyp->ps[pi].x == polyp->ps[pi + 1].x
00146                 && polyp->ps[pi].y == polyp->ps[pi + 1].y)
00147                 continue;
00148             pnls[pnll].pp = &polyp->ps[pi];
00149             pnls[pnll].link = &pnls[pnll % polyp->pn];
00150             pnlps[pnll] = &pnls[pnll];
00151             pnll++;
00152         }
00153     } else {
00154         for (pi = 0; pi < polyp->pn; pi++) {
00155             if (pi > 0 && polyp->ps[pi].x == polyp->ps[pi - 1].x &&
00156                 polyp->ps[pi].y == polyp->ps[pi - 1].y)
00157                 continue;
00158             pnls[pnll].pp = &polyp->ps[pi];
00159             pnls[pnll].link = &pnls[pnll % polyp->pn];
00160             pnlps[pnll] = &pnls[pnll];
00161             pnll++;
00162         }
00163     }
00164 
00165 #if DEBUG >= 1
00166     fprintf(stderr, "points\n%d\n", pnll);
00167     for (pnli = 0; pnli < pnll; pnli++)
00168         fprintf(stderr, "%f %f\n", pnls[pnli].pp->x, pnls[pnli].pp->y);
00169 #endif
00170 
00171     /* generate list of triangles */
00172     triangulate(pnlps, pnll);
00173 
00174 #if DEBUG >= 2
00175     fprintf(stderr, "triangles\n%d\n", tril);
00176     for (trii = 0; trii < tril; trii++)
00177         for (ei = 0; ei < 3; ei++)
00178             fprintf(stderr, "%f %f\n", tris[trii].e[ei].pnl0p->pp->x,
00179                     tris[trii].e[ei].pnl0p->pp->y);
00180 #endif
00181 
00182     /* connect all pairs of triangles that share an edge */
00183     for (trii = 0; trii < tril; trii++)
00184         for (trij = trii + 1; trij < tril; trij++)
00185             connecttris(trii, trij);
00186 
00187     /* find first and last triangles */
00188     for (trii = 0; trii < tril; trii++)
00189         if (pointintri(trii, &eps[0]))
00190             break;
00191     if (trii == tril) {
00192         prerror("source point not in any triangle");
00193         return -1;
00194     }
00195     ftrii = trii;
00196     for (trii = 0; trii < tril; trii++)
00197         if (pointintri(trii, &eps[1]))
00198             break;
00199     if (trii == tril) {
00200         prerror("destination point not in any triangle");
00201         return -1;
00202     }
00203     ltrii = trii;
00204 
00205     /* mark the strip of triangles from eps[0] to eps[1] */
00206     if (!marktripath(ftrii, ltrii)) {
00207         prerror("cannot find triangle path");
00208         /* a straight line is better than failing */
00209         growops(2);
00210         output->pn = 2;
00211         ops[0] = eps[0], ops[1] = eps[1];
00212         output->ps = ops;
00213         return 0;
00214     }
00215 
00216     /* if endpoints in same triangle, use a single line */
00217     if (ftrii == ltrii) {
00218         growops(2);
00219         output->pn = 2;
00220         ops[0] = eps[0], ops[1] = eps[1];
00221         output->ps = ops;
00222         return 0;
00223     }
00224 
00225     /* build funnel and shortest path linked list (in add2dq) */
00226     epnls[0].pp = &eps[0], epnls[0].link = NULL;
00227     epnls[1].pp = &eps[1], epnls[1].link = NULL;
00228     add2dq(DQ_FRONT, &epnls[0]);
00229     dq.apex = dq.fpnlpi;
00230     trii = ftrii;
00231     while (trii != -1) {
00232         trip = &tris[trii];
00233         trip->mark = 2;
00234 
00235         /* find the left and right points of the exiting edge */
00236         for (ei = 0; ei < 3; ei++)
00237             if (trip->e[ei].rtp && trip->e[ei].rtp->mark == 1)
00238                 break;
00239         if (ei == 3) {          /* in last triangle */
00240             if (ccw(&eps[1], dq.pnlps[dq.fpnlpi]->pp,
00241                     dq.pnlps[dq.lpnlpi]->pp) == ISCCW)
00242                 lpnlp = dq.pnlps[dq.lpnlpi], rpnlp = &epnls[1];
00243             else
00244                 lpnlp = &epnls[1], rpnlp = dq.pnlps[dq.lpnlpi];
00245         } else {
00246             pnlp = trip->e[(ei + 1) % 3].pnl1p;
00247             if (ccw(trip->e[ei].pnl0p->pp, pnlp->pp,
00248                     trip->e[ei].pnl1p->pp) == ISCCW)
00249                 lpnlp = trip->e[ei].pnl1p, rpnlp = trip->e[ei].pnl0p;
00250             else
00251                 lpnlp = trip->e[ei].pnl0p, rpnlp = trip->e[ei].pnl1p;
00252         }
00253 
00254         /* update deque */
00255         if (trii == ftrii) {
00256             add2dq(DQ_BACK, lpnlp);
00257             add2dq(DQ_FRONT, rpnlp);
00258         } else {
00259             if (dq.pnlps[dq.fpnlpi] != rpnlp
00260                 && dq.pnlps[dq.lpnlpi] != rpnlp) {
00261                 /* add right point to deque */
00262                 splitindex = finddqsplit(rpnlp);
00263                 splitdq(DQ_BACK, splitindex);
00264                 add2dq(DQ_FRONT, rpnlp);
00265                 /* if the split is behind the apex, then reset apex */
00266                 if (splitindex > dq.apex)
00267                     dq.apex = splitindex;
00268             } else {
00269                 /* add left point to deque */
00270                 splitindex = finddqsplit(lpnlp);
00271                 splitdq(DQ_FRONT, splitindex);
00272                 add2dq(DQ_BACK, lpnlp);
00273                 /* if the split is in front of the apex, then reset apex */
00274                 if (splitindex < dq.apex)
00275                     dq.apex = splitindex;
00276             }
00277         }
00278         trii = -1;
00279         for (ei = 0; ei < 3; ei++)
00280             if (trip->e[ei].rtp && trip->e[ei].rtp->mark == 1) {
00281                 trii = trip->e[ei].rtp - tris;
00282                 break;
00283             }
00284     }
00285 
00286 #if DEBUG >= 1
00287     fprintf(stderr, "polypath");
00288     for (pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
00289         fprintf(stderr, " %f %f", pnlp->pp->x, pnlp->pp->y);
00290     fprintf(stderr, "\n");
00291 #endif
00292 
00293     for (pi = 0, pnlp = &epnls[1]; pnlp; pnlp = pnlp->link)
00294         pi++;
00295     growops(pi);
00296     output->pn = pi;
00297     for (pi = pi - 1, pnlp = &epnls[1]; pnlp; pi--, pnlp = pnlp->link)
00298         ops[pi] = *pnlp->pp;
00299     output->ps = ops;
00300 
00301     return 0;
00302 }
00303 
00304 /* triangulate polygon */
00305 static void triangulate(pointnlink_t ** pnlps, int pnln)
00306 {
00307     int pnli, pnlip1, pnlip2;
00308 
00309         if (pnln > 3) 
00310         {
00311                 for (pnli = 0; pnli < pnln; pnli++) 
00312                 {
00313                         pnlip1 = (pnli + 1) % pnln;
00314                         pnlip2 = (pnli + 2) % pnln;
00315                         if (isdiagonal(pnli, pnlip2, pnlps, pnln)) 
00316                         {
00317                                 loadtriangle(pnlps[pnli], pnlps[pnlip1], pnlps[pnlip2]);
00318                                 for (pnli = pnlip1; pnli < pnln - 1; pnli++)
00319                                         pnlps[pnli] = pnlps[pnli + 1];
00320                                 triangulate(pnlps, pnln - 1);
00321                                 return;
00322                         }
00323                 }
00324                 prerror("triangulation failed");
00325     } 
00326         else
00327                 loadtriangle(pnlps[0], pnlps[1], pnlps[2]);
00328 }
00329 
00330 /* check if (i, i + 2) is a diagonal */
00331 static int isdiagonal(int pnli, int pnlip2, pointnlink_t ** pnlps,
00332                       int pnln)
00333 {
00334     int pnlip1, pnlim1, pnlj, pnljp1, res;
00335 
00336     /* neighborhood test */
00337     pnlip1 = (pnli + 1) % pnln;
00338     pnlim1 = (pnli + pnln - 1) % pnln;
00339     /* If P[pnli] is a convex vertex [ pnli+1 left of (pnli-1,pnli) ]. */
00340     if (ccw(pnlps[pnlim1]->pp, pnlps[pnli]->pp, pnlps[pnlip1]->pp) ==
00341         ISCCW)
00342         res =
00343             (ccw(pnlps[pnli]->pp, pnlps[pnlip2]->pp, pnlps[pnlim1]->pp) ==
00344              ISCCW)
00345             && (ccw(pnlps[pnlip2]->pp, pnlps[pnli]->pp, pnlps[pnlip1]->pp)
00346                 == ISCCW);
00347     /* Assume (pnli - 1, pnli, pnli + 1) not collinear. */
00348     else
00349         res = (ccw(pnlps[pnli]->pp, pnlps[pnlip2]->pp,
00350                    pnlps[pnlip1]->pp) == ISCW);
00351     if (!res)
00352         return FALSE;
00353 
00354     /* check against all other edges */
00355     for (pnlj = 0; pnlj < pnln; pnlj++) {
00356         pnljp1 = (pnlj + 1) % pnln;
00357         if (!((pnlj == pnli) || (pnljp1 == pnli) ||
00358               (pnlj == pnlip2) || (pnljp1 == pnlip2)))
00359             if (intersects(pnlps[pnli]->pp, pnlps[pnlip2]->pp,
00360                            pnlps[pnlj]->pp, pnlps[pnljp1]->pp))
00361                 return FALSE;
00362     }
00363     return TRUE;
00364 }
00365 
00366 static void loadtriangle(pointnlink_t * pnlap, pointnlink_t * pnlbp,
00367                          pointnlink_t * pnlcp)
00368 {
00369     triangle_t *trip;
00370     int ei;
00371 
00372     /* make space */
00373     if (tril >= trin)
00374         growtris(trin + 20);
00375     trip = &tris[tril++];
00376     trip->mark = 0;
00377     trip->e[0].pnl0p = pnlap, trip->e[0].pnl1p = pnlbp, trip->e[0].rtp =
00378         NULL;
00379     trip->e[1].pnl0p = pnlbp, trip->e[1].pnl1p = pnlcp, trip->e[1].rtp =
00380         NULL;
00381     trip->e[2].pnl0p = pnlcp, trip->e[2].pnl1p = pnlap, trip->e[2].rtp =
00382         NULL;
00383     for (ei = 0; ei < 3; ei++)
00384         trip->e[ei].ltp = trip;
00385 }
00386 
00387 /* connect a pair of triangles at their common edge (if any) */
00388 static void connecttris(int tri1, int tri2)
00389 {
00390     triangle_t *tri1p, *tri2p;
00391     int ei, ej;
00392 
00393     for (ei = 0; ei < 3; ei++) {
00394         for (ej = 0; ej < 3; ej++) {
00395             tri1p = &tris[tri1], tri2p = &tris[tri2];
00396             if ((tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl0p->pp &&
00397                  tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl1p->pp) ||
00398                 (tri1p->e[ei].pnl0p->pp == tri2p->e[ej].pnl1p->pp &&
00399                  tri1p->e[ei].pnl1p->pp == tri2p->e[ej].pnl0p->pp))
00400                 tri1p->e[ei].rtp = tri2p, tri2p->e[ej].rtp = tri1p;
00401         }
00402     }
00403 }
00404 
00405 /* find and mark path from trii, to trij */
00406 static int marktripath(int trii, int trij)
00407 {
00408     int ei;
00409 
00410     if (tris[trii].mark)
00411         return FALSE;
00412     tris[trii].mark = 1;
00413     if (trii == trij)
00414         return TRUE;
00415     for (ei = 0; ei < 3; ei++)
00416         if (tris[trii].e[ei].rtp &&
00417             marktripath(tris[trii].e[ei].rtp - tris, trij))
00418             return TRUE;
00419     tris[trii].mark = 0;
00420     return FALSE;
00421 }
00422 
00423 /* add a new point to the deque, either front or back */
00424 static void add2dq(int side, pointnlink_t * pnlp)
00425 {
00426     if (side == DQ_FRONT) {
00427         if (dq.lpnlpi - dq.fpnlpi >= 0)
00428             pnlp->link = dq.pnlps[dq.fpnlpi];   /* shortest path links */
00429         dq.fpnlpi--;
00430         dq.pnlps[dq.fpnlpi] = pnlp;
00431     } else {
00432         if (dq.lpnlpi - dq.fpnlpi >= 0)
00433             pnlp->link = dq.pnlps[dq.lpnlpi];   /* shortest path links */
00434         dq.lpnlpi++;
00435         dq.pnlps[dq.lpnlpi] = pnlp;
00436     }
00437 }
00438 
00439 static void splitdq(int side, int index)
00440 {
00441     if (side == DQ_FRONT)
00442         dq.lpnlpi = index;
00443     else
00444         dq.fpnlpi = index;
00445 }
00446 
00447 static int finddqsplit(pointnlink_t * pnlp)
00448 {
00449     int index;
00450 
00451     for (index = dq.fpnlpi; index < dq.apex; index++)
00452         if (ccw(dq.pnlps[index + 1]->pp, dq.pnlps[index]->pp, pnlp->pp) ==
00453             ISCCW)
00454             return index;
00455     for (index = dq.lpnlpi; index > dq.apex; index--)
00456         if (ccw(dq.pnlps[index - 1]->pp, dq.pnlps[index]->pp, pnlp->pp) ==
00457             ISCW)
00458             return index;
00459     return dq.apex;
00460 }
00461 
00462 /* ccw test: CCW, CW, or co-linear */
00463 static int ccw(Ppoint_t * p1p, Ppoint_t * p2p, Ppoint_t * p3p)
00464 {
00465     double d;
00466 
00467     d = ((p1p->y - p2p->y) * (p3p->x - p2p->x)) -
00468         ((p3p->y - p2p->y) * (p1p->x - p2p->x));
00469     return (d > 0) ? ISCCW : ((d < 0) ? ISCW : ISON);
00470 }
00471 
00472 /* line to line intersection */
00473 static int intersects(Ppoint_t * pap, Ppoint_t * pbp,
00474                       Ppoint_t * pcp, Ppoint_t * pdp)
00475 {
00476     int ccw1, ccw2, ccw3, ccw4;
00477 
00478     if (ccw(pap, pbp, pcp) == ISON || ccw(pap, pbp, pdp) == ISON ||
00479         ccw(pcp, pdp, pap) == ISON || ccw(pcp, pdp, pbp) == ISON) {
00480         if (between(pap, pbp, pcp) || between(pap, pbp, pdp) ||
00481             between(pcp, pdp, pap) || between(pcp, pdp, pbp))
00482             return TRUE;
00483     } else {
00484         ccw1 = (ccw(pap, pbp, pcp) == ISCCW) ? 1 : 0;
00485         ccw2 = (ccw(pap, pbp, pdp) == ISCCW) ? 1 : 0;
00486         ccw3 = (ccw(pcp, pdp, pap) == ISCCW) ? 1 : 0;
00487         ccw4 = (ccw(pcp, pdp, pbp) == ISCCW) ? 1 : 0;
00488         return (ccw1 ^ ccw2) && (ccw3 ^ ccw4);
00489     }
00490     return FALSE;
00491 }
00492 
00493 /* is pbp between pap and pcp */
00494 static int between(Ppoint_t * pap, Ppoint_t * pbp, Ppoint_t * pcp)
00495 {
00496     Ppoint_t p1, p2;
00497 
00498     p1.x = pbp->x - pap->x, p1.y = pbp->y - pap->y;
00499     p2.x = pcp->x - pap->x, p2.y = pcp->y - pap->y;
00500     if (ccw(pap, pbp, pcp) != ISON)
00501         return FALSE;
00502     return (p2.x * p1.x + p2.y * p1.y >= 0) &&
00503         (p2.x * p2.x + p2.y * p2.y <= p1.x * p1.x + p1.y * p1.y);
00504 }
00505 
00506 static int pointintri(int trii, Ppoint_t * pp)
00507 {
00508     int ei, sum;
00509 
00510     for (ei = 0, sum = 0; ei < 3; ei++)
00511         if (ccw(tris[trii].e[ei].pnl0p->pp,
00512                 tris[trii].e[ei].pnl1p->pp, pp) != ISCW)
00513             sum++;
00514     return (sum == 3 || sum == 0);
00515 }
00516 
00517 static void growpnls(int newpnln)
00518 {
00519     if (newpnln <= pnln)
00520         return;
00521     if (!pnls) {
00522         if (!(pnls = (pointnlink_t *) malloc(POINTNLINKSIZE * newpnln))) {
00523             prerror("cannot malloc pnls");
00524             longjmp(jbuf,1);
00525         }
00526         if (!(pnlps = (pointnlink_t **) malloc(POINTNLINKPSIZE * newpnln))) {
00527             prerror("cannot malloc pnlps");
00528             longjmp(jbuf,1);
00529         }
00530     } else {
00531         if (!(pnls = (pointnlink_t *) realloc((void *) pnls,
00532                                               POINTNLINKSIZE * newpnln))) {
00533             prerror("cannot realloc pnls");
00534             longjmp(jbuf,1);
00535         }
00536         if (!(pnlps = (pointnlink_t **) realloc((void *) pnlps,
00537                                                 POINTNLINKPSIZE *
00538                                                 newpnln))) {
00539             prerror("cannot realloc pnlps");
00540             longjmp(jbuf,1);
00541         }
00542     }
00543     pnln = newpnln;
00544 }
00545 
00546 static void growtris(int newtrin)
00547 {
00548     if (newtrin <= trin)
00549         return;
00550     if (!tris) {
00551         if (!(tris = (triangle_t *) malloc(TRIANGLESIZE * newtrin))) {
00552             prerror("cannot malloc tris");
00553             longjmp(jbuf,1);
00554         }
00555     } else {
00556         if (!(tris = (triangle_t *) realloc((void *) tris,
00557                                             TRIANGLESIZE * newtrin))) {
00558             prerror("cannot realloc tris");
00559             longjmp(jbuf,1);
00560         }
00561     }
00562     trin = newtrin;
00563 }
00564 
00565 static void growdq(int newdqn)
00566 {
00567     if (newdqn <= dq.pnlpn)
00568         return;
00569     if (!dq.pnlps) {
00570         if (!
00571             (dq.pnlps =
00572              (pointnlink_t **) malloc(POINTNLINKPSIZE * newdqn))) {
00573             prerror("cannot malloc dq.pnls");
00574             longjmp(jbuf,1);
00575         }
00576     } else {
00577         if (!(dq.pnlps = (pointnlink_t **) realloc((void *) dq.pnlps,
00578                                                    POINTNLINKPSIZE *
00579                                                    newdqn))) {
00580             prerror("cannot realloc dq.pnls");
00581             longjmp(jbuf,1);
00582         }
00583     }
00584     dq.pnlpn = newdqn;
00585 }
00586 
00587 static void growops(int newopn)
00588 {
00589     if (newopn <= opn)
00590         return;
00591     if (!ops) {
00592         if (!(ops = (Ppoint_t *) malloc(POINTSIZE * newopn))) {
00593             prerror("cannot malloc ops");
00594             longjmp(jbuf,1);
00595         }
00596     } else {
00597         if (!(ops = (Ppoint_t *) realloc((void *) ops,
00598                                          POINTSIZE * newopn))) {
00599             prerror("cannot realloc ops");
00600             longjmp(jbuf,1);
00601         }
00602     }
00603     opn = newopn;
00604 }