|
Graphviz
2.29.20120524.0446
|
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 }
1.7.5