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