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