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