25
* Return midpoint between two given points.
27
static point midPt(point p, point q)
31
v.x = (p.x + q.x) / 2;
32
v.y = (p.y + q.y) / 2;
37
* Convert a point to its string representation.
39
static char *p2s(point p, char *buf)
41
sprintf(buf, "(%d,%d)", p.x, p.y);
24
* Convert a pointf to its string representation.
26
static char *pf2s(pointf p, char *buf)
28
sprintf(buf, "(%.5g,%.5g)", p.x, p.y);
86
75
char ppbuf[100], cpbuf[100], llbuf[100], urbuf[100];
89
"segment [%s,%s] does not intersect box ll=%s,ur=%s\n",
90
p2s(pp, ppbuf), p2s(cp, cpbuf), p2s(ll, llbuf), p2s(ur,
78
"segment [%s,%s] does not intersect box ll=%s,ur=%s\n",
79
pf2s(pp, ppbuf), pf2s(cp, cpbuf),
80
pf2s(ll, llbuf), pf2s(ur, urbuf));
98
87
* Returns true if p is on or in box bb
100
static int inBox(point p, box * bb)
89
static int inBoxf(pointf p, boxf * bb)
102
return ((p.x >= bb->LL.x) && (p.x <= bb->UR.x) &&
103
(p.y >= bb->LL.y) && (p.y <= bb->UR.y));
91
return INSIDE(p, *bb);
123
115
/* The following functions are derived from pp. 411-415 (pp. 791-795)
124
116
* of Graphics Gems. In the code there, they use a SGN function to
125
117
* count crossings. This doesn't seem to handle certain special cases,
126
* as when the last point is on the line. It certainly doesn't work
127
* for use; see bug 145. We need to use ZSGN instead.
118
* as when the last point is on the line. It certainly didn't work
119
* for us when we used int values; see bug 145. We needed to use CMP instead.
121
* Possibly unnecessary with double values, but harmless.
129
#define SGN(a,b) (((a)<(b)) ? -1 : 1)
130
#define ZSGN(a,b) (((a)<(b)) ? -1 : (a)>(b) ? 1 : 0)
132
124
/* countVertCross:
133
125
* Return the number of times the Bezier control polygon crosses
134
126
* the vertical line x = xcoord.
136
static int countVertCross(pointf * pts, int xcoord)
128
static int countVertCross(pointf * pts, double xcoord)
139
131
int sign, old_sign;
140
132
int num_crossings = 0;
142
sign = old_sign = ZSGN(pts[0].x, xcoord);
134
sign = CMP(pts[0].x, xcoord);
145
137
for (i = 1; i <= 3; i++) {
146
sign = ZSGN(pts[i].x, xcoord);
139
sign = CMP(pts[i].x, xcoord);
147
140
if ((sign != old_sign) && (old_sign != 0))
152
143
return num_crossings;
156
146
/* countHorzCross:
157
147
* Return the number of times the Bezier control polygon crosses
158
148
* the horizontal line y = ycoord.
160
static int countHorzCross(pointf * pts, int ycoord)
150
static int countHorzCross(pointf * pts, double ycoord)
163
153
int sign, old_sign;
164
154
int num_crossings = 0;
166
sign = old_sign = ZSGN(pts[0].y, ycoord);
156
sign = CMP(pts[0].y, ycoord);
169
159
for (i = 1; i <= 3; i++) {
170
sign = ZSGN(pts[i].y, ycoord);
161
sign = CMP(pts[i].y, ycoord);
171
162
if ((sign != old_sign) && (old_sign != 0))
176
165
return num_crossings;
252
240
return findHorizontal(Right, (tmin + tmax) / 2.0, tmax, ycoord, xmin,
257
#define P2PF(p, pf) (pf.x = p.x, pf.y = p.y)
258
#define PF2P(pf, p) (p.x = ROUND (pf.x), p.y = ROUND (pf.y))
261
245
* Given four spline control points and a box,
262
246
* find the shortest portion of the spline from
263
* ipts[0] to the intersection with the box, if any.
264
* If an intersection is found, the four points are stored in ipts[0..3]
265
* with ipts[3] being on the box, and 1 is returned. Otherwise, ipts
247
* pts[0] to the intersection with the box, if any.
248
* If an intersection is found, the four points are stored in pts[0..3]
249
* with pts[3] being on the box, and 1 is returned. Otherwise, pts
266
250
* is left unchanged and 0 is returned.
268
static int splineIntersect(point * ipts, box * bb)
252
static int splineIntersectf(pointf * pts, boxf * bb)
270
254
double tmin = 2.0;
273
256
pointf origpts[4];
276
259
for (i = 0; i < 4; i++) {
277
P2PF(ipts[i], origpts[i]);
281
263
t = findVertical(pts, 0.0, 1.0, bb->LL.x, bb->LL.y, bb->UR.y);
345
323
/* at present, we only handle single spline case */
346
324
if (ED_spl(e)->size > 1) {
347
325
agerr(AGWARN, "%s -> %s: spline size > 1 not supported\n",
348
e->tail->name, e->head->name);
326
agnameof(agtail(e)), agnameof(aghead(e)));
351
329
bez = ED_spl(e)->list;
352
330
size = bez->size;
357
335
/* allocate new Bezier */
358
336
nbez = GNEW(bezier);
373
351
bb = &(GD_bb(lh));
374
if (!inBox(ND_coord_i(head), bb)) {
352
if (!inBoxf(ND_coord(head), bb)) {
375
353
agerr(AGWARN, "%s -> %s: head not inside head cluster %s\n",
376
e->tail->name, e->head->name, agget(e, "lhead"));
354
agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "lhead"));
378
356
/* If first control point is in bb, degenerate case. Spline
379
357
* reduces to four points between the arrow head and the point
380
358
* where the segment between the first control point and arrow head
383
if (inBox(bez->list[0], bb)) {
384
if (inBox(ND_coord_i(tail), bb)) {
361
if (inBoxf(bez->list[0], bb)) {
362
if (inBoxf(ND_coord(tail), bb)) {
386
364
"%s -> %s: tail is inside head cluster %s\n",
387
e->tail->name, e->head->name, agget(e, "lhead"));
365
agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "lhead"));
389
367
assert(bez->sflag); /* must be arrowhead on tail */
390
p = boxIntersect(bez->list[0], bez->sp, bb);
368
p = boxIntersectf(bez->list[0], bez->sp, bb);
391
369
bez->list[3] = p;
392
bez->list[1] = midPt(p, bez->sp);
393
bez->list[0] = midPt(bez->list[1], bez->sp);
394
bez->list[2] = midPt(bez->list[1], p);
370
bez->list[1] = mid_pointf(p, bez->sp);
371
bez->list[0] = mid_pointf(bez->list[1], bez->sp);
372
bez->list[2] = mid_pointf(bez->list[1], p);
397
arrowEndClip(e, bez->list,
374
endi = arrowEndClip(e, bez->list,
398
375
starti, 0, nbez, bez->eflag);
403
380
for (endi = 0; endi < size - 1; endi += 3) {
404
if (splineIntersect(&(bez->list[endi]), bb))
381
if (splineIntersectf(&(bez->list[endi]), bb))
407
384
if (endi == size - 1) { /* no intersection */
408
385
assert(bez->eflag);
409
nbez->ep = boxIntersect(bez->ep, bez->list[endi], bb);
386
nbez->ep = boxIntersectf(bez->ep, bez->list[endi], bb);
434
411
bb = &(GD_bb(lt));
435
if (!inBox(ND_coord_i(tail), bb)) {
412
if (!inBoxf(ND_coord(tail), bb)) {
436
413
agerr(AGWARN, "%s -> %s: tail not inside tail cluster %s\n",
437
e->tail->name, head->name, agget(e, "ltail"));
414
agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "ltail"));
439
416
/* If last control point is in bb, degenerate case. Spline
440
417
* reduces to four points between arrow head, and the point
441
418
* where the segment between the last control point and the
442
419
* arrow head crosses box.
444
if (inBox(bez->list[endi], bb)) {
445
if (inBox(ND_coord_i(head), bb)) {
421
if (inBoxf(bez->list[endi], bb)) {
422
if (inBoxf(ND_coord(head), bb)) {
447
"%s -> %s: head is inside tail cluster %s\n",
448
e->tail->name, e->head->name, agget(e, "ltail"));
424
"%s -> %s: head is inside tail cluster %s\n",
425
agnameof(agtail(e)), agnameof(aghead(e)), agget(e, "ltail"));
450
427
assert(bez->eflag); /* must be arrowhead on head */
451
p = boxIntersect(bez->list[endi], nbez->ep, bb);
428
p = boxIntersectf(bez->list[endi], nbez->ep, bb);
452
429
starti = endi - 3;
453
430
bez->list[starti] = p;
454
bez->list[starti + 2] = midPt(p, nbez->ep);
455
bez->list[starti + 3] =
456
midPt(bez->list[starti + 2], nbez->ep);
457
bez->list[starti + 1] =
458
midPt(bez->list[starti + 2], p);
431
bez->list[starti + 2] = mid_pointf(p, nbez->ep);
432
bez->list[starti + 3] = mid_pointf(bez->list[starti + 2], nbez->ep);
433
bez->list[starti + 1] = mid_pointf(bez->list[starti + 2], p);
461
arrowStartClip(e, bez->list,
462
starti, endi - 3, nbez,
435
starti = arrowStartClip(e, bez->list, starti,
436
endi - 3, nbez, bez->sflag);
467
440
for (starti = endi; starti > 0; starti -= 3) {
468
441
for (i = 0; i < 4; i++)
469
442
pts[i] = bez->list[starti - i];
470
if (splineIntersect(pts, bb)) {
443
if (splineIntersectf(pts, bb)) {
471
444
for (i = 0; i < 4; i++)
472
445
bez->list[starti - i] = pts[i];
476
449
if (starti == 0) {
477
450
assert(bez->sflag);
479
boxIntersect(bez->sp, bez->list[starti], bb);
452
boxIntersectf(bez->sp, bez->list[starti], bb);
484
arrowStartClip(e, bez->list,
485
starti, endi - 3, nbez,
456
starti = arrowStartClip(e, bez->list, starti,
457
endi - 3, nbez, bez->sflag);
498
469
/* complete Bezier, free garbage and attach new Bezier to edge
500
471
nbez->size = endi - starti + 1;
501
nbez->list = N_GNEW(nbez->size, point);
472
nbez->list = N_GNEW(nbez->size, pointf);
502
473
for (i = 0, j = starti; i < nbez->size; i++, j++)
503
474
nbez->list[i] = bez->list[j];
506
477
ED_spl(e)->list = nbez;
510
480
/* dot_compoundEdges: