Graphviz  2.29.20120524.0446
lib/neatogen/poly.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 /* poly.c
00015  */
00016 
00017 #include "neato.h"
00018 #include <assert.h>
00019 #include <string.h>
00020 #include <math.h>
00021 #include "poly.h"
00022 #include "geom.h"
00023 #include "mem.h"
00024 
00025 #define BOX 1
00026 #define ISBOX(p) ((p)->kind & BOX)
00027 #define CIRCLE 2
00028 #define ISCIRCLE(p) ((p)->kind & CIRCLE)
00029 
00030 static int maxcnt = 0;
00031 static Point *tp1 = NULL;
00032 static Point *tp2 = NULL;
00033 static Point *tp3 = NULL;
00034 
00035 void polyFree()
00036 {
00037     maxcnt = 0;
00038     free(tp1);
00039     free(tp2);
00040     free(tp3);
00041     tp1 = NULL;
00042     tp2 = NULL;
00043     tp3 = NULL;
00044 }
00045 
00046 void breakPoly(Poly * pp)
00047 {
00048     free(pp->verts);
00049 }
00050 
00051 static void bbox(Point * verts, int cnt, Point * o, Point * c)
00052 {
00053     double xmin, ymin, xmax, ymax;
00054     int i;
00055 
00056     xmin = xmax = verts->x;
00057     ymin = ymax = verts->y;
00058     for (i = 1; i < cnt; i++) {
00059         verts++;
00060         if (verts->x < xmin)
00061             xmin = verts->x;
00062         if (verts->y < ymin)
00063             ymin = verts->y;
00064         if (verts->x > xmax)
00065             xmax = verts->x;
00066         if (verts->y > ymax)
00067             ymax = verts->y;
00068     }
00069     o->x = xmin;
00070     o->y = ymin;
00071     c->x = xmax;
00072     c->y = ymax;
00073 }
00074 
00075 #ifdef OLD
00076 static void inflate(Point * prev, Point * cur, Point * next, double margin)
00077 {
00078     double theta = atan2(prev->y - cur->y, prev->x - cur->x);
00079     double phi = atan2(next->y - cur->y, next->x - cur->x);
00080     double beta = (theta + phi) / 2.0;
00081     double gamma = (M_PI + phi - theta) / 2.0;
00082     double denom;
00083 
00084     denom = cos(gamma);
00085     cur->x -= margin * (cos(beta)) / denom;
00086     cur->y -= margin * (sin(beta)) / denom;
00087 }
00088 
00089 static void inflatePts(Point * verts, int cnt, double margin)
00090 {
00091     int i;
00092     Point first;
00093     Point savepoint;
00094     Point prevpoint;
00095     Point *prev = &prevpoint;
00096     Point *cur;
00097     Point *next;
00098 
00099     first = verts[0];
00100     prevpoint = verts[cnt - 1];
00101     cur = &verts[0];
00102     next = &verts[1];
00103     for (i = 0; i < cnt - 1; i++) {
00104         savepoint = *cur;
00105         inflate(prev, cur, next, margin);
00106         cur++;
00107         next++;
00108         prevpoint = savepoint;
00109     }
00110 
00111     next = &first;
00112     inflate(prev, cur, next, margin);
00113 }
00114 #else
00115 static void inflatePts(Point * verts, int cnt, float xmargin, float ymargin)
00116 {
00117     int i;
00118     Point *cur;
00119 
00120     cur = &verts[0];
00121     for (i = 0; i < cnt; i++) {
00122         cur->x *= xmargin;
00123         cur->y *= ymargin;
00124         cur++;
00125     }
00126 }
00127 #endif
00128 
00129 static int isBox(Point * verts, int cnt)
00130 {
00131     if (cnt != 4)
00132         return 0;
00133 
00134     if (verts[0].y == verts[1].y)
00135         return ((verts[2].y == verts[3].y) &&
00136                 (verts[0].x == verts[3].x) && (verts[1].x == verts[2].x));
00137     else
00138         return ((verts[0].x == verts[1].x) &&
00139                 (verts[2].x == verts[3].x) &&
00140                 (verts[0].y == verts[3].y) && (verts[1].y == verts[2].y));
00141 }
00142 
00143 static Point makeScaledTransPoint(int x, int y, float dx, float dy)
00144 {
00145     Point rv;
00146     rv.x = PS2INCH(x) + dx;
00147     rv.y = PS2INCH(y) + dy;
00148     return rv;
00149 }
00150 
00151 static Point makeScaledPoint(double x, double y)
00152 {
00153     Point rv;
00154     rv.x = PS2INCH(x);
00155     rv.y = PS2INCH(y);
00156     return rv;
00157 }
00158 
00159 static Point *genRound(Agnode_t * n, int *sidep, float xm, float ym)
00160 {
00161     int sides = 0;
00162     Point *verts;
00163     char *p = agget(n, "samplepoints");
00164     int i;
00165 
00166     if (p)
00167         sides = atoi(p);
00168     if (sides < 3)
00169         sides = DFLT_SAMPLE;
00170     verts = N_GNEW(sides, Point);
00171     for (i = 0; i < sides; i++) {
00172         verts[i].x =
00173             (ND_width(n) / 2.0 + xm) * cos(i / (double) sides * M_PI * 2.0);
00174         verts[i].y =
00175             (ND_height(n) / 2.0 + ym) * sin(i / (double) sides * M_PI * 2.0);
00176     }
00177     *sidep = sides;
00178     return verts;
00179 }
00180 
00181 #define PUTPT(P,X,Y) ((P).x=(X),(P).y=(Y))
00182 
00183 int makeAddPoly(Poly * pp, Agnode_t * n, float xmargin, float ymargin)
00184 {
00185     int i;
00186     int sides;
00187     Point *verts;
00188     polygon_t *poly;
00189     boxf b;
00190 
00191     if (ND_clust(n)) {
00192         Point b;
00193         sides = 4;
00194         b.x = ND_width(n) / 2.0 + xmargin;
00195         b.y = ND_height(n) / 2.0 + ymargin;
00196         pp->kind = BOX;
00197         verts = N_GNEW(sides, Point);
00198         PUTPT(verts[0], b.x, b.y);
00199         PUTPT(verts[1], -b.x, b.y);
00200         PUTPT(verts[2], -b.x, -b.y);
00201         PUTPT(verts[3], b.x, -b.y);
00202     } else
00203         switch (shapeOf(n)) {
00204         case SH_POLY:
00205             poly = (polygon_t *) ND_shape_info(n);
00206             sides = poly->sides;
00207 
00208             if (streq(ND_shape(n)->name, "box"))
00209                 pp->kind = BOX;
00210             else if (streq(ND_shape(n)->name, "polygon")
00211                      && isBox(poly->vertices, sides))
00212                 pp->kind = BOX;
00213             else if ((poly->sides < 3) && poly->regular)
00214                 pp->kind = CIRCLE;
00215             else
00216                 pp->kind = 0;
00217 
00218             if (sides >= 3) {   /* real polygon */
00219                 verts = N_GNEW(sides, Point);
00220                 if (pp->kind == BOX) {
00221                         /* To do an additive margin, we rely on knowing that
00222                          * the vertices are CCW starting from the UR
00223                          */
00224                     verts[0].x = PS2INCH(poly->vertices[0].x) + xmargin;
00225                     verts[0].y = PS2INCH(poly->vertices[0].y) + ymargin;
00226                     verts[1].x = PS2INCH(poly->vertices[1].x) - xmargin;
00227                     verts[1].y = PS2INCH(poly->vertices[1].y) + ymargin;
00228                     verts[2].x = PS2INCH(poly->vertices[2].x) - xmargin;
00229                     verts[2].y = PS2INCH(poly->vertices[2].y) - ymargin;
00230                     verts[3].x = PS2INCH(poly->vertices[3].x) + xmargin;
00231                     verts[3].y = PS2INCH(poly->vertices[3].y) - ymargin;
00232  
00233                 }
00234                 else {
00235                     for (i = 0; i < sides; i++) {
00236                         double h = LEN(poly->vertices[i].x,poly->vertices[i].y);
00237                         verts[i].x = poly->vertices[i].x * (1.0 + xmargin/h);
00238                         verts[i].y = poly->vertices[i].y * (1.0 + ymargin/h);
00239                         verts[i].x = PS2INCH(verts[i].x);
00240                         verts[i].y = PS2INCH(verts[i].y);
00241                     }
00242                 }
00243             } else
00244                 verts = genRound(n, &sides, xmargin, ymargin);
00245             break;
00246         case SH_RECORD:
00247             sides = 4;
00248             verts = N_GNEW(sides, Point);
00249             b = ((field_t *) ND_shape_info(n))->b;
00250             verts[0] = makeScaledTransPoint(b.LL.x, b.LL.y, -xmargin, -ymargin);
00251             verts[1] = makeScaledTransPoint(b.UR.x, b.LL.y, xmargin, -ymargin);
00252             verts[2] = makeScaledTransPoint(b.UR.x, b.UR.y, xmargin, ymargin);
00253             verts[3] = makeScaledTransPoint(b.LL.x, b.UR.y, -xmargin, ymargin);
00254             pp->kind = BOX;
00255             break;
00256         case SH_POINT:
00257             pp->kind = CIRCLE;
00258             verts = genRound(n, &sides, xmargin, ymargin);
00259             break;
00260         default:
00261             agerr(AGERR, "makeAddPoly: unknown shape type %s\n",
00262                   ND_shape(n)->name);
00263             return 1;
00264         }
00265 
00266     pp->verts = verts;
00267     pp->nverts = sides;
00268     bbox(verts, sides, &pp->origin, &pp->corner);
00269 
00270     if (sides > maxcnt)
00271         maxcnt = sides;
00272     return 0;
00273 }
00274 
00275 int makePoly(Poly * pp, Agnode_t * n, float xmargin, float ymargin)
00276 {
00277     int i;
00278     int sides;
00279     Point *verts;
00280     polygon_t *poly;
00281     boxf b;
00282 
00283     if (ND_clust(n)) {
00284         Point b;
00285         sides = 4;
00286         b.x = ND_width(n) / 2.0;
00287         b.y = ND_height(n) / 2.0;
00288         pp->kind = BOX;
00289         verts = N_GNEW(sides, Point);
00290         PUTPT(verts[0], b.x, b.y);
00291         PUTPT(verts[1], -b.x, b.y);
00292         PUTPT(verts[2], -b.x, -b.y);
00293         PUTPT(verts[3], b.x, -b.y);
00294     } else
00295         switch (shapeOf(n)) {
00296         case SH_POLY:
00297             poly = (polygon_t *) ND_shape_info(n);
00298             sides = poly->sides;
00299             if (sides >= 3) {   /* real polygon */
00300                 verts = N_GNEW(sides, Point);
00301                 for (i = 0; i < sides; i++) {
00302                     verts[i].x = PS2INCH(poly->vertices[i].x);
00303                     verts[i].y = PS2INCH(poly->vertices[i].y);
00304                 }
00305             } else
00306                 verts = genRound(n, &sides, 0, 0);
00307 
00308             if (streq(ND_shape(n)->name, "box"))
00309                 pp->kind = BOX;
00310             else if (streq(ND_shape(n)->name, "polygon")
00311                      && isBox(verts, sides))
00312                 pp->kind = BOX;
00313             else if ((poly->sides < 3) && poly->regular)
00314                 pp->kind = CIRCLE;
00315             else
00316                 pp->kind = 0;
00317 
00318             break;
00319         case SH_RECORD:
00320             sides = 4;
00321             verts = N_GNEW(sides, Point);
00322             b = ((field_t *) ND_shape_info(n))->b;
00323             verts[0] = makeScaledPoint(b.LL.x, b.LL.y);
00324             verts[1] = makeScaledPoint(b.UR.x, b.LL.y);
00325             verts[2] = makeScaledPoint(b.UR.x, b.UR.y);
00326             verts[3] = makeScaledPoint(b.LL.x, b.UR.y);
00327             pp->kind = BOX;
00328             break;
00329         case SH_POINT:
00330             pp->kind = CIRCLE;
00331             verts = genRound(n, &sides, 0, 0);
00332             break;
00333         default:
00334             agerr(AGERR, "makePoly: unknown shape type %s\n",
00335                   ND_shape(n)->name);
00336             return 1;
00337         }
00338 
00339 #ifdef OLD
00340     if (margin != 0.0)
00341         inflatePts(verts, sides, margin);
00342 #else
00343     if ((xmargin != 1.0) || (ymargin != 1.0))
00344         inflatePts(verts, sides, xmargin, ymargin);
00345 #endif
00346 
00347     pp->verts = verts;
00348     pp->nverts = sides;
00349     bbox(verts, sides, &pp->origin, &pp->corner);
00350 
00351     if (sides > maxcnt)
00352         maxcnt = sides;
00353     return 0;
00354 }
00355 
00356 static int
00357 pintersect(Point originp, Point cornerp, Point originq, Point cornerq)
00358 {
00359     return ((originp.x <= cornerq.x) && (originq.x <= cornerp.x) &&
00360             (originp.y <= cornerq.y) && (originq.y <= cornerp.y));
00361 }
00362 
00363 #define Pin 1
00364 #define Qin 2
00365 #define Unknown 0
00366 
00367 #define advance(A,B,N) (B++, A = (A+1)%N)
00368 
00369 static int edgesIntersect(Point * P, Point * Q, int n, int m)
00370 {
00371     int a = 0;
00372     int b = 0;
00373     int aa = 0;
00374     int ba = 0;
00375     int a1, b1;
00376     Point A, B;
00377     double cross;
00378     int bHA, aHB;
00379     Point p;
00380     int inflag = Unknown;
00381     /* int      i = 0; */
00382     /* int      Reset = 0; */
00383 
00384     do {
00385         a1 = (a + n - 1) % n;
00386         b1 = (b + m - 1) % m;
00387 
00388         subpt(&A, P[a], P[a1]);
00389         subpt(&B, Q[b], Q[b1]);
00390 
00391         cross = area_2(origin, A, B);
00392         bHA = leftOf(P[a1], P[a], Q[b]);
00393         aHB = leftOf(Q[b1], Q[b], P[a]);
00394 
00395         /* If A & B intersect, update inflag. */
00396         if (intersection(P[a1], P[a], Q[b1], Q[b], &p))
00397             return 1;
00398 
00399         /* Advance rules. */
00400         if ((cross == 0) && !bHA && !aHB) {
00401             if (inflag == Pin)
00402                 advance(b, ba, m);
00403             else
00404                 advance(a, aa, n);
00405         } else if (cross >= 0)
00406             if (bHA)
00407                 advance(a, aa, n);
00408             else {
00409                 advance(b, ba, m);
00410         } else {                /* if ( cross < 0 ) */
00411 
00412             if (aHB)
00413                 advance(b, ba, m);
00414             else
00415                 advance(a, aa, n);
00416         }
00417 
00418     } while (((aa < n) || (ba < m)) && (aa < 2 * n) && (ba < 2 * m));
00419 
00420     return 0;
00421 
00422 }
00423 
00424 /* inPoly:
00425  * Return 1 if q is inside polygon vertex[]
00426  * Assume points are in CCW order
00427  */
00428 static int inPoly(Point vertex[], int n, Point q)
00429 {
00430     int i, i1;                  /* point index; i1 = i-1 mod n */
00431     double x;                   /* x intersection of e with ray */
00432     double crossings = 0;       /* number of edge/ray crossings */
00433 
00434     if (tp3 == NULL)
00435         tp3 = N_GNEW(maxcnt, Point);
00436 
00437     /* Shift so that q is the origin. */
00438     for (i = 0; i < n; i++) {
00439         tp3[i].x = vertex[i].x - q.x;
00440         tp3[i].y = vertex[i].y - q.y;
00441     }
00442 
00443     /* For each edge e=(i-1,i), see if crosses ray. */
00444     for (i = 0; i < n; i++) {
00445         i1 = (i + n - 1) % n;
00446 
00447         /* if edge is horizontal, test to see if the point is on it */
00448         if ((tp3[i].y == 0) && (tp3[i1].y == 0)) {
00449             if ((tp3[i].x * tp3[i1].x) < 0) {
00450                 return 1;
00451             } else {
00452                 continue;
00453             }
00454         }
00455 
00456         /* if e straddles the x-axis... */
00457         if (((tp3[i].y >= 0) && (tp3[i1].y <= 0)) ||
00458             ((tp3[i1].y >= 0) && (tp3[i].y <= 0))) {
00459             /* e straddles ray, so compute intersection with ray. */
00460             x = (tp3[i].x * tp3[i1].y - tp3[i1].x * tp3[i].y)
00461                 / (double) (tp3[i1].y - tp3[i].y);
00462 
00463             /* if intersect at origin, we've found intersection */
00464             if (x == 0)
00465                 return 1;;
00466 
00467             /* crosses ray if strictly positive intersection. */
00468             if (x > 0) {
00469                 if ((tp3[i].y == 0) || (tp3[i1].y == 0)) {
00470                     crossings += .5;    /* goes thru vertex */
00471                 } else {
00472                     crossings += 1.0;
00473                 }
00474             }
00475         }
00476     }
00477 
00478     /* q inside if an odd number of crossings. */
00479     if ((((int) crossings) % 2) == 1)
00480         return 1;
00481     else
00482         return 0;
00483 }
00484 
00485 static int inBox(Point p, Point origin, Point corner)
00486 {
00487     return ((p.x <= corner.x) &&
00488             (p.x >= origin.x) && (p.y <= corner.y) && (p.y >= origin.y));
00489 
00490 }
00491 
00492 static void transCopy(Point * inp, int cnt, Point off, Point * outp)
00493 {
00494     int i;
00495 
00496     for (i = 0; i < cnt; i++) {
00497         outp->x = inp->x + off.x;
00498         outp->y = inp->y + off.y;
00499         inp++;
00500         outp++;
00501     }
00502 }
00503 
00504 int polyOverlap(Point p, Poly * pp, Point q, Poly * qp)
00505 {
00506     Point op, cp;
00507     Point oq, cq;
00508 
00509     /* translate bounding boxes */
00510     addpt(&op, p, pp->origin);
00511     addpt(&cp, p, pp->corner);
00512     addpt(&oq, q, qp->origin);
00513     addpt(&cq, q, qp->corner);
00514 
00515     /* If bounding boxes don't overlap, done */
00516     if (!pintersect(op, cp, oq, cq))
00517         return 0;
00518 
00519     if (ISBOX(pp) && ISBOX(qp))
00520         return 1;
00521     if (ISCIRCLE(pp) && ISCIRCLE(qp)) {
00522         double d =
00523             (pp->corner.x - pp->origin.x + qp->corner.x - qp->origin.x);
00524         double dx = p.x - q.x;
00525         double dy = p.y - q.y;
00526         if ((dx * dx + dy * dy) > (d * d) / 4.0)
00527             return 0;
00528         else
00529             return 1;
00530     }
00531 
00532     if (tp1 == NULL) {
00533         tp1 = N_GNEW(maxcnt, Point);
00534         tp2 = N_GNEW(maxcnt, Point);
00535     }
00536 
00537     transCopy(pp->verts, pp->nverts, p, tp1);
00538     transCopy(qp->verts, qp->nverts, q, tp2);
00539     return (edgesIntersect(tp1, tp2, pp->nverts, qp->nverts) ||
00540             (inBox(*tp1, oq, cq) && inPoly(tp2, qp->nverts, *tp1)) ||
00541             (inBox(*tp2, op, cp) && inPoly(tp1, pp->nverts, *tp2)));
00542 }