1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | /* poly.c |
15 | */ |
16 | |
17 | #include "neato.h" |
18 | #include <assert.h> |
19 | #include <string.h> |
20 | #include <math.h> |
21 | #include "poly.h" |
22 | #include "geom.h" |
23 | #include "mem.h" |
24 | |
25 | #define BOX 1 |
26 | #define ISBOX(p) ((p)->kind & BOX) |
27 | #define CIRCLE 2 |
28 | #define ISCIRCLE(p) ((p)->kind & CIRCLE) |
29 | |
30 | static int maxcnt = 0; |
31 | static Point *tp1 = NULL; |
32 | static Point *tp2 = NULL; |
33 | static Point *tp3 = NULL; |
34 | |
35 | void polyFree() |
36 | { |
37 | maxcnt = 0; |
38 | free(tp1); |
39 | free(tp2); |
40 | free(tp3); |
41 | tp1 = NULL; |
42 | tp2 = NULL; |
43 | tp3 = NULL; |
44 | } |
45 | |
46 | void breakPoly(Poly * pp) |
47 | { |
48 | free(pp->verts); |
49 | } |
50 | |
51 | static void bbox(Point * verts, int cnt, Point * o, Point * c) |
52 | { |
53 | double xmin, ymin, xmax, ymax; |
54 | int i; |
55 | |
56 | xmin = xmax = verts->x; |
57 | ymin = ymax = verts->y; |
58 | for (i = 1; i < cnt; i++) { |
59 | verts++; |
60 | if (verts->x < xmin) |
61 | xmin = verts->x; |
62 | if (verts->y < ymin) |
63 | ymin = verts->y; |
64 | if (verts->x > xmax) |
65 | xmax = verts->x; |
66 | if (verts->y > ymax) |
67 | ymax = verts->y; |
68 | } |
69 | o->x = xmin; |
70 | o->y = ymin; |
71 | c->x = xmax; |
72 | c->y = ymax; |
73 | } |
74 | |
75 | #ifdef OLD |
76 | static void inflate(Point * prev, Point * cur, Point * next, double margin) |
77 | { |
78 | double theta = atan2(prev->y - cur->y, prev->x - cur->x); |
79 | double phi = atan2(next->y - cur->y, next->x - cur->x); |
80 | double beta = (theta + phi) / 2.0; |
81 | double gamma = (M_PI + phi - theta) / 2.0; |
82 | double denom; |
83 | |
84 | denom = cos(gamma); |
85 | cur->x -= margin * (cos(beta)) / denom; |
86 | cur->y -= margin * (sin(beta)) / denom; |
87 | } |
88 | |
89 | static void inflatePts(Point * verts, int cnt, double margin) |
90 | { |
91 | int i; |
92 | Point first; |
93 | Point savepoint; |
94 | Point prevpoint; |
95 | Point *prev = &prevpoint; |
96 | Point *cur; |
97 | Point *next; |
98 | |
99 | first = verts[0]; |
100 | prevpoint = verts[cnt - 1]; |
101 | cur = &verts[0]; |
102 | next = &verts[1]; |
103 | for (i = 0; i < cnt - 1; i++) { |
104 | savepoint = *cur; |
105 | inflate(prev, cur, next, margin); |
106 | cur++; |
107 | next++; |
108 | prevpoint = savepoint; |
109 | } |
110 | |
111 | next = &first; |
112 | inflate(prev, cur, next, margin); |
113 | } |
114 | #else |
115 | static void inflatePts(Point * verts, int cnt, float xmargin, float ymargin) |
116 | { |
117 | int i; |
118 | Point *cur; |
119 | |
120 | cur = &verts[0]; |
121 | for (i = 0; i < cnt; i++) { |
122 | cur->x *= xmargin; |
123 | cur->y *= ymargin; |
124 | cur++; |
125 | } |
126 | } |
127 | #endif |
128 | |
129 | static int isBox(Point * verts, int cnt) |
130 | { |
131 | if (cnt != 4) |
132 | return 0; |
133 | |
134 | if (verts[0].y == verts[1].y) |
135 | return ((verts[2].y == verts[3].y) && |
136 | (verts[0].x == verts[3].x) && (verts[1].x == verts[2].x)); |
137 | else |
138 | return ((verts[0].x == verts[1].x) && |
139 | (verts[2].x == verts[3].x) && |
140 | (verts[0].y == verts[3].y) && (verts[1].y == verts[2].y)); |
141 | } |
142 | |
143 | static Point makeScaledTransPoint(int x, int y, float dx, float dy) |
144 | { |
145 | Point rv; |
146 | rv.x = PS2INCH(x) + dx; |
147 | rv.y = PS2INCH(y) + dy; |
148 | return rv; |
149 | } |
150 | |
151 | static Point makeScaledPoint(double x, double y) |
152 | { |
153 | Point rv; |
154 | rv.x = PS2INCH(x); |
155 | rv.y = PS2INCH(y); |
156 | return rv; |
157 | } |
158 | |
159 | static Point *genRound(Agnode_t * n, int *sidep, float xm, float ym) |
160 | { |
161 | int sides = 0; |
162 | Point *verts; |
163 | char *p = agget(n, "samplepoints" ); |
164 | int i; |
165 | |
166 | if (p) |
167 | sides = atoi(p); |
168 | if (sides < 3) |
169 | sides = DFLT_SAMPLE; |
170 | verts = N_GNEW(sides, Point); |
171 | for (i = 0; i < sides; i++) { |
172 | verts[i].x = |
173 | (ND_width(n) / 2.0 + xm) * cos(i / (double) sides * M_PI * 2.0); |
174 | verts[i].y = |
175 | (ND_height(n) / 2.0 + ym) * sin(i / (double) sides * M_PI * 2.0); |
176 | } |
177 | *sidep = sides; |
178 | return verts; |
179 | } |
180 | |
181 | #define PUTPT(P,X,Y) ((P).x=(X),(P).y=(Y)) |
182 | |
183 | int makeAddPoly(Poly * pp, Agnode_t * n, float xmargin, float ymargin) |
184 | { |
185 | int i; |
186 | int sides; |
187 | Point *verts; |
188 | polygon_t *poly; |
189 | boxf b; |
190 | |
191 | if (ND_clust(n)) { |
192 | Point b; |
193 | sides = 4; |
194 | b.x = ND_width(n) / 2.0 + xmargin; |
195 | b.y = ND_height(n) / 2.0 + ymargin; |
196 | pp->kind = BOX; |
197 | verts = N_GNEW(sides, Point); |
198 | PUTPT(verts[0], b.x, b.y); |
199 | PUTPT(verts[1], -b.x, b.y); |
200 | PUTPT(verts[2], -b.x, -b.y); |
201 | PUTPT(verts[3], b.x, -b.y); |
202 | } else |
203 | switch (shapeOf(n)) { |
204 | case SH_POLY: |
205 | poly = (polygon_t *) ND_shape_info(n); |
206 | sides = poly->sides; |
207 | |
208 | if (streq(ND_shape(n)->name, "box" )) |
209 | pp->kind = BOX; |
210 | else if (streq(ND_shape(n)->name, "polygon" ) |
211 | && isBox(poly->vertices, sides)) |
212 | pp->kind = BOX; |
213 | else if ((poly->sides < 3) && poly->regular) |
214 | pp->kind = CIRCLE; |
215 | else |
216 | pp->kind = 0; |
217 | |
218 | if (sides >= 3) { /* real polygon */ |
219 | verts = N_GNEW(sides, Point); |
220 | if (pp->kind == BOX) { |
221 | /* To do an additive margin, we rely on knowing that |
222 | * the vertices are CCW starting from the UR |
223 | */ |
224 | verts[0].x = PS2INCH(poly->vertices[0].x) + xmargin; |
225 | verts[0].y = PS2INCH(poly->vertices[0].y) + ymargin; |
226 | verts[1].x = PS2INCH(poly->vertices[1].x) - xmargin; |
227 | verts[1].y = PS2INCH(poly->vertices[1].y) + ymargin; |
228 | verts[2].x = PS2INCH(poly->vertices[2].x) - xmargin; |
229 | verts[2].y = PS2INCH(poly->vertices[2].y) - ymargin; |
230 | verts[3].x = PS2INCH(poly->vertices[3].x) + xmargin; |
231 | verts[3].y = PS2INCH(poly->vertices[3].y) - ymargin; |
232 | |
233 | } |
234 | else { |
235 | for (i = 0; i < sides; i++) { |
236 | double h = LEN(poly->vertices[i].x,poly->vertices[i].y); |
237 | verts[i].x = poly->vertices[i].x * (1.0 + xmargin/h); |
238 | verts[i].y = poly->vertices[i].y * (1.0 + ymargin/h); |
239 | verts[i].x = PS2INCH(verts[i].x); |
240 | verts[i].y = PS2INCH(verts[i].y); |
241 | } |
242 | } |
243 | } else |
244 | verts = genRound(n, &sides, xmargin, ymargin); |
245 | break; |
246 | case SH_RECORD: |
247 | sides = 4; |
248 | verts = N_GNEW(sides, Point); |
249 | b = ((field_t *) ND_shape_info(n))->b; |
250 | verts[0] = makeScaledTransPoint(b.LL.x, b.LL.y, -xmargin, -ymargin); |
251 | verts[1] = makeScaledTransPoint(b.UR.x, b.LL.y, xmargin, -ymargin); |
252 | verts[2] = makeScaledTransPoint(b.UR.x, b.UR.y, xmargin, ymargin); |
253 | verts[3] = makeScaledTransPoint(b.LL.x, b.UR.y, -xmargin, ymargin); |
254 | pp->kind = BOX; |
255 | break; |
256 | case SH_POINT: |
257 | pp->kind = CIRCLE; |
258 | verts = genRound(n, &sides, xmargin, ymargin); |
259 | break; |
260 | default: |
261 | agerr(AGERR, "makeAddPoly: unknown shape type %s\n" , |
262 | ND_shape(n)->name); |
263 | return 1; |
264 | } |
265 | |
266 | pp->verts = verts; |
267 | pp->nverts = sides; |
268 | bbox(verts, sides, &pp->origin, &pp->corner); |
269 | |
270 | if (sides > maxcnt) |
271 | maxcnt = sides; |
272 | return 0; |
273 | } |
274 | |
275 | int makePoly(Poly * pp, Agnode_t * n, float xmargin, float ymargin) |
276 | { |
277 | int i; |
278 | int sides; |
279 | Point *verts; |
280 | polygon_t *poly; |
281 | boxf b; |
282 | |
283 | if (ND_clust(n)) { |
284 | Point b; |
285 | sides = 4; |
286 | b.x = ND_width(n) / 2.0; |
287 | b.y = ND_height(n) / 2.0; |
288 | pp->kind = BOX; |
289 | verts = N_GNEW(sides, Point); |
290 | PUTPT(verts[0], b.x, b.y); |
291 | PUTPT(verts[1], -b.x, b.y); |
292 | PUTPT(verts[2], -b.x, -b.y); |
293 | PUTPT(verts[3], b.x, -b.y); |
294 | } else |
295 | switch (shapeOf(n)) { |
296 | case SH_POLY: |
297 | poly = (polygon_t *) ND_shape_info(n); |
298 | sides = poly->sides; |
299 | if (sides >= 3) { /* real polygon */ |
300 | verts = N_GNEW(sides, Point); |
301 | for (i = 0; i < sides; i++) { |
302 | verts[i].x = PS2INCH(poly->vertices[i].x); |
303 | verts[i].y = PS2INCH(poly->vertices[i].y); |
304 | } |
305 | } else |
306 | verts = genRound(n, &sides, 0, 0); |
307 | |
308 | if (streq(ND_shape(n)->name, "box" )) |
309 | pp->kind = BOX; |
310 | else if (streq(ND_shape(n)->name, "polygon" ) |
311 | && isBox(verts, sides)) |
312 | pp->kind = BOX; |
313 | else if ((poly->sides < 3) && poly->regular) |
314 | pp->kind = CIRCLE; |
315 | else |
316 | pp->kind = 0; |
317 | |
318 | break; |
319 | case SH_RECORD: |
320 | sides = 4; |
321 | verts = N_GNEW(sides, Point); |
322 | b = ((field_t *) ND_shape_info(n))->b; |
323 | verts[0] = makeScaledPoint(b.LL.x, b.LL.y); |
324 | verts[1] = makeScaledPoint(b.UR.x, b.LL.y); |
325 | verts[2] = makeScaledPoint(b.UR.x, b.UR.y); |
326 | verts[3] = makeScaledPoint(b.LL.x, b.UR.y); |
327 | pp->kind = BOX; |
328 | break; |
329 | case SH_POINT: |
330 | pp->kind = CIRCLE; |
331 | verts = genRound(n, &sides, 0, 0); |
332 | break; |
333 | default: |
334 | agerr(AGERR, "makePoly: unknown shape type %s\n" , |
335 | ND_shape(n)->name); |
336 | return 1; |
337 | } |
338 | |
339 | #ifdef OLD |
340 | if (margin != 0.0) |
341 | inflatePts(verts, sides, margin); |
342 | #else |
343 | if ((xmargin != 1.0) || (ymargin != 1.0)) |
344 | inflatePts(verts, sides, xmargin, ymargin); |
345 | #endif |
346 | |
347 | pp->verts = verts; |
348 | pp->nverts = sides; |
349 | bbox(verts, sides, &pp->origin, &pp->corner); |
350 | |
351 | if (sides > maxcnt) |
352 | maxcnt = sides; |
353 | return 0; |
354 | } |
355 | |
356 | static int |
357 | pintersect(Point originp, Point cornerp, Point originq, Point cornerq) |
358 | { |
359 | return ((originp.x <= cornerq.x) && (originq.x <= cornerp.x) && |
360 | (originp.y <= cornerq.y) && (originq.y <= cornerp.y)); |
361 | } |
362 | |
363 | #define Pin 1 |
364 | #define Qin 2 |
365 | #define Unknown 0 |
366 | |
367 | #define advance(A,B,N) (B++, A = (A+1)%N) |
368 | |
369 | static int edgesIntersect(Point * P, Point * Q, int n, int m) |
370 | { |
371 | int a = 0; |
372 | int b = 0; |
373 | int aa = 0; |
374 | int ba = 0; |
375 | int a1, b1; |
376 | Point A, B; |
377 | double cross; |
378 | int bHA, aHB; |
379 | Point p; |
380 | int inflag = Unknown; |
381 | /* int i = 0; */ |
382 | /* int Reset = 0; */ |
383 | |
384 | do { |
385 | a1 = (a + n - 1) % n; |
386 | b1 = (b + m - 1) % m; |
387 | |
388 | subpt(&A, P[a], P[a1]); |
389 | subpt(&B, Q[b], Q[b1]); |
390 | |
391 | cross = area_2(origin, A, B); |
392 | bHA = leftOf(P[a1], P[a], Q[b]); |
393 | aHB = leftOf(Q[b1], Q[b], P[a]); |
394 | |
395 | /* If A & B intersect, update inflag. */ |
396 | if (intersection(P[a1], P[a], Q[b1], Q[b], &p)) |
397 | return 1; |
398 | |
399 | /* Advance rules. */ |
400 | if ((cross == 0) && !bHA && !aHB) { |
401 | if (inflag == Pin) |
402 | advance(b, ba, m); |
403 | else |
404 | advance(a, aa, n); |
405 | } else if (cross >= 0) |
406 | if (bHA) |
407 | advance(a, aa, n); |
408 | else { |
409 | advance(b, ba, m); |
410 | } else { /* if ( cross < 0 ) */ |
411 | |
412 | if (aHB) |
413 | advance(b, ba, m); |
414 | else |
415 | advance(a, aa, n); |
416 | } |
417 | |
418 | } while (((aa < n) || (ba < m)) && (aa < 2 * n) && (ba < 2 * m)); |
419 | |
420 | return 0; |
421 | |
422 | } |
423 | |
424 | /* inPoly: |
425 | * Return 1 if q is inside polygon vertex[] |
426 | * Assume points are in CCW order |
427 | */ |
428 | static int inPoly(Point vertex[], int n, Point q) |
429 | { |
430 | int i, i1; /* point index; i1 = i-1 mod n */ |
431 | double x; /* x intersection of e with ray */ |
432 | double crossings = 0; /* number of edge/ray crossings */ |
433 | |
434 | if (tp3 == NULL) |
435 | tp3 = N_GNEW(maxcnt, Point); |
436 | |
437 | /* Shift so that q is the origin. */ |
438 | for (i = 0; i < n; i++) { |
439 | tp3[i].x = vertex[i].x - q.x; |
440 | tp3[i].y = vertex[i].y - q.y; |
441 | } |
442 | |
443 | /* For each edge e=(i-1,i), see if crosses ray. */ |
444 | for (i = 0; i < n; i++) { |
445 | i1 = (i + n - 1) % n; |
446 | |
447 | /* if edge is horizontal, test to see if the point is on it */ |
448 | if ((tp3[i].y == 0) && (tp3[i1].y == 0)) { |
449 | if ((tp3[i].x * tp3[i1].x) < 0) { |
450 | return 1; |
451 | } else { |
452 | continue; |
453 | } |
454 | } |
455 | |
456 | /* if e straddles the x-axis... */ |
457 | if (((tp3[i].y >= 0) && (tp3[i1].y <= 0)) || |
458 | ((tp3[i1].y >= 0) && (tp3[i].y <= 0))) { |
459 | /* e straddles ray, so compute intersection with ray. */ |
460 | x = (tp3[i].x * tp3[i1].y - tp3[i1].x * tp3[i].y) |
461 | / (double) (tp3[i1].y - tp3[i].y); |
462 | |
463 | /* if intersect at origin, we've found intersection */ |
464 | if (x == 0) |
465 | return 1;; |
466 | |
467 | /* crosses ray if strictly positive intersection. */ |
468 | if (x > 0) { |
469 | if ((tp3[i].y == 0) || (tp3[i1].y == 0)) { |
470 | crossings += .5; /* goes through vertex */ |
471 | } else { |
472 | crossings += 1.0; |
473 | } |
474 | } |
475 | } |
476 | } |
477 | |
478 | /* q inside if an odd number of crossings. */ |
479 | if ((((int) crossings) % 2) == 1) |
480 | return 1; |
481 | else |
482 | return 0; |
483 | } |
484 | |
485 | static int inBox(Point p, Point origin, Point corner) |
486 | { |
487 | return ((p.x <= corner.x) && |
488 | (p.x >= origin.x) && (p.y <= corner.y) && (p.y >= origin.y)); |
489 | |
490 | } |
491 | |
492 | static void transCopy(Point * inp, int cnt, Point off, Point * outp) |
493 | { |
494 | int i; |
495 | |
496 | for (i = 0; i < cnt; i++) { |
497 | outp->x = inp->x + off.x; |
498 | outp->y = inp->y + off.y; |
499 | inp++; |
500 | outp++; |
501 | } |
502 | } |
503 | |
504 | int polyOverlap(Point p, Poly * pp, Point q, Poly * qp) |
505 | { |
506 | Point op, cp; |
507 | Point oq, cq; |
508 | |
509 | /* translate bounding boxes */ |
510 | addpt(&op, p, pp->origin); |
511 | addpt(&cp, p, pp->corner); |
512 | addpt(&oq, q, qp->origin); |
513 | addpt(&cq, q, qp->corner); |
514 | |
515 | /* If bounding boxes don't overlap, done */ |
516 | if (!pintersect(op, cp, oq, cq)) |
517 | return 0; |
518 | |
519 | if (ISBOX(pp) && ISBOX(qp)) |
520 | return 1; |
521 | if (ISCIRCLE(pp) && ISCIRCLE(qp)) { |
522 | double d = |
523 | (pp->corner.x - pp->origin.x + qp->corner.x - qp->origin.x); |
524 | double dx = p.x - q.x; |
525 | double dy = p.y - q.y; |
526 | if ((dx * dx + dy * dy) > (d * d) / 4.0) |
527 | return 0; |
528 | else |
529 | return 1; |
530 | } |
531 | |
532 | if (tp1 == NULL) { |
533 | tp1 = N_GNEW(maxcnt, Point); |
534 | tp2 = N_GNEW(maxcnt, Point); |
535 | } |
536 | |
537 | transCopy(pp->verts, pp->nverts, p, tp1); |
538 | transCopy(qp->verts, qp->nverts, q, tp2); |
539 | return (edgesIntersect(tp1, tp2, pp->nverts, qp->nverts) || |
540 | (inBox(*tp1, oq, cq) && inPoly(tp2, qp->nverts, *tp1)) || |
541 | (inBox(*tp2, op, cp) && inPoly(tp1, pp->nverts, *tp2))); |
542 | } |
543 | |