| #include <stdio.h> |
| #include <math.h> |
| #include "pic.h" |
| #include "y.tab.h" |
| |
| obj* |
| arcgen(int type) /* handles circular and (eventually) elliptical arcs */ |
| { |
| static double prevw = HT10; |
| static double prevh = HT5; |
| static double prevrad = HT2; |
| static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 }; |
| static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 }; |
| static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 }; |
| static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 }; |
| static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR }; |
| double dx2, dy2, ht, phi, r, d; |
| int i, head, to, at, cw, invis, ddtype; |
| obj *p, *ppos; |
| double fromx, fromy, tox, toy; |
| Attr *ap; |
| |
| tox = 0; |
| toy = 0; |
| prevrad = getfval("arcrad"); |
| prevh = getfval("arrowht"); |
| prevw = getfval("arrowwid"); |
| fromx = curx; |
| fromy = cury; |
| head = to = at = cw = invis = ddtype = 0; |
| for (i = 0; i < nattr; i++) { |
| ap = &attr[i]; |
| switch (ap->a_type) { |
| case TEXTATTR: |
| savetext(ap->a_sub, ap->a_val.p); |
| break; |
| case HEAD: |
| head += ap->a_val.i; |
| break; |
| case INVIS: |
| invis = INVIS; |
| break; |
| case HEIGHT: /* length of arrowhead */ |
| prevh = ap->a_val.f; |
| break; |
| case WIDTH: /* width of arrowhead */ |
| prevw = ap->a_val.f; |
| break; |
| case RADIUS: |
| prevrad = ap->a_val.f; |
| break; |
| case DIAMETER: |
| prevrad = ap->a_val.f / 2; |
| break; |
| case CW: |
| cw = 1; |
| break; |
| case FROM: /* start point of arc */ |
| ppos = ap->a_val.o; |
| fromx = ppos->o_x; |
| fromy = ppos->o_y; |
| break; |
| case TO: /* end point of arc */ |
| ppos = ap->a_val.o; |
| tox = ppos->o_x; |
| toy = ppos->o_y; |
| to++; |
| break; |
| case AT: /* center of arc */ |
| ppos = ap->a_val.o; |
| curx = ppos->o_x; |
| cury = ppos->o_y; |
| at = 1; |
| break; |
| case UP: |
| hvmode = U_DIR; |
| break; |
| case DOWN: |
| hvmode = D_DIR; |
| break; |
| case RIGHT: |
| hvmode = R_DIR; |
| break; |
| case LEFT: |
| hvmode = L_DIR; |
| break; |
| } |
| } |
| if (!at && !to) { /* the defaults are mostly OK */ |
| curx = fromx + prevrad * dctrx[cw][hvmode]; |
| cury = fromy + prevrad * dctry[cw][hvmode]; |
| tox = fromx + prevrad * dtox[cw][hvmode]; |
| toy = fromy + prevrad * dtoy[cw][hvmode]; |
| hvmode = nexthv[cw][hvmode]; |
| } |
| else if (!at) { |
| dx2 = (tox - fromx) / 2; |
| dy2 = (toy - fromy) / 2; |
| phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2); |
| if (prevrad <= 0.0) |
| prevrad = dx2*dx2+dy2*dy2; |
| for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2) |
| ; /* this kludge gets around too-small radii */ |
| prevrad = r; |
| ht = sqrt(d); |
| curx = fromx + dx2 + ht * cos(phi); |
| cury = fromy + dy2 + ht * sin(phi); |
| dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n", |
| dx2, dy2, phi, r, ht); |
| } |
| else if (at && !to) { /* do we have all the cases??? */ |
| tox = fromx + prevrad * dtox[cw][hvmode]; |
| toy = fromy + prevrad * dtoy[cw][hvmode]; |
| hvmode = nexthv[cw][hvmode]; |
| } |
| if (cw) { /* interchange roles of from-to and heads */ |
| double temp; |
| temp = fromx; fromx = tox; tox = temp; |
| temp = fromy; fromy = toy; toy = temp; |
| if (head == HEAD1) |
| head = HEAD2; |
| else if (head == HEAD2) |
| head = HEAD1; |
| } |
| p = makenode(type, 7); |
| arc_extreme(fromx, fromy, tox, toy, curx, cury); |
| p->o_val[0] = fromx; |
| p->o_val[1] = fromy; |
| p->o_val[2] = tox; |
| p->o_val[3] = toy; |
| if (cw) { |
| curx = fromx; |
| cury = fromy; |
| } else { |
| curx = tox; |
| cury = toy; |
| } |
| p->o_val[4] = prevw; |
| p->o_val[5] = prevh; |
| p->o_val[6] = prevrad; |
| p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype; |
| if (head) |
| p->o_nhead = getfval("arrowhead"); |
| dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n", |
| prevrad, p->o_x, p->o_y, |
| p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]); |
| return(p); |
| } |
| |
| /*************************************************************************** |
| bounding box of a circular arc Eric Grosse 24 May 84 |
| |
| Conceptually, this routine generates a list consisting of the start, |
| end, and whichever north, east, south, and west points lie on the arc. |
| The bounding box is then the range of this list. |
| list = {start,end} |
| j = quadrant(start) |
| k = quadrant(end) |
| if( j==k && long way 'round ) append north,west,south,east |
| else |
| while( j != k ) |
| append center+radius*[j-th of north,west,south,east unit vectors] |
| j += 1 (mod 4) |
| return( bounding box of list ) |
| The following code implements this, with simple optimizations. |
| ***********************************************************************/ |
| |
| |
| void |
| arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc) |
| { |
| /* assumes center isn't too far out */ |
| double r, xmin, ymin, xmax, ymax; |
| int j, k; |
| x0 -= xc; y0 -= yc; /* move to center */ |
| x1 -= xc; y1 -= yc; |
| xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1; |
| xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1; |
| r = sqrt(x0*x0 + y0*y0); |
| if (r > 0.0) { |
| j = quadrant(x0,y0); |
| k = quadrant(x1,y1); |
| if (j == k && y1*x0 < x1*y0) { |
| /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */ |
| if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r; |
| if( xmax < r) xmax = r; if( ymax < r) ymax = r; |
| } else { |
| while (j != k) { |
| switch (j) { |
| case 1: if( ymax < r) ymax = r; break; /* north */ |
| case 2: if( xmin > -r) xmin = -r; break; /* west */ |
| case 3: if( ymin > -r) ymin = -r; break; /* south */ |
| case 4: if( xmax < r) xmax = r; break; /* east */ |
| } |
| j = j%4 + 1; |
| } |
| } |
| } |
| xmin += xc; ymin += yc; |
| xmax += xc; ymax += yc; |
| extreme(xmin, ymin); |
| extreme(xmax, ymax); |
| } |
| |
| int |
| quadrant(double x, double y) |
| { |
| if ( x>=0.0 && y> 0.0) return(1); |
| else if( x< 0.0 && y>=0.0) return(2); |
| else if( x<=0.0 && y< 0.0) return(3); |
| else if( x> 0.0 && y<=0.0) return(4); |
| else return 0; /* shut up lint */ |
| } |
| |