1 #ifndef lint 2 static char sccsid[] = "@(#)arcgen.c 3.1 (CWI) 85/07/30"; 3 #endif lint 4 5 #include <stdio.h> 6 #include <math.h> 7 #include "pic.h" 8 #include "y.tab.h" 9 10 obj *arcgen(type) /* handles circular and (eventually) elliptical arcs */ 11 { 12 static float prevw = HT10; 13 static float prevh = HT5; 14 static float prevrad = HT2; 15 static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 }; 16 static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 }; 17 static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 }; 18 static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 }; 19 static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR }; 20 float dx2, dy2, ht, phi, r, d, ddval; 21 int i, head, to, at, cw, invis, ddtype; 22 obj *p, *ppos; 23 float fromx, fromy, tox, toy; 24 Attr *ap; 25 26 prevrad = getfval("arcrad"); 27 prevh = getfval("arrowht"); 28 prevw = getfval("arrowwid"); 29 fromx = curx; 30 fromy = cury; 31 head = to = at = cw = invis = ddtype = 0; 32 for (i = 0; i < nattr; i++) { 33 ap = &attr[i]; 34 switch (ap->a_type) { 35 case TEXTATTR: 36 savetext(ap->a_sub, ap->a_val.p); 37 break; 38 case HEAD: 39 head += ap->a_val.i; 40 break; 41 case INVIS: 42 invis = INVIS; 43 break; 44 case DOT: 45 case DASH: 46 ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT; 47 if (ap->a_sub == DEFAULT) 48 ddval = getfval("dashwid"); 49 else 50 ddval = ap->a_val.f; 51 break; 52 case HEIGHT: /* length of arrowhead */ 53 prevh = ap->a_val.f; 54 break; 55 case WIDTH: /* width of arrowhead */ 56 prevw = ap->a_val.f; 57 break; 58 case RADIUS: 59 prevrad = ap->a_val.f; 60 break; 61 case DIAMETER: 62 prevrad = ap->a_val.f / 2; 63 break; 64 case CW: 65 cw = 1; 66 break; 67 case FROM: /* start point of arc */ 68 ppos = ap->a_val.o; 69 fromx = ppos->o_x; 70 fromy = ppos->o_y; 71 break; 72 case TO: /* end point of arc */ 73 ppos = ap->a_val.o; 74 tox = ppos->o_x; 75 toy = ppos->o_y; 76 to++; 77 break; 78 case AT: /* center of arc */ 79 ppos = ap->a_val.o; 80 curx = ppos->o_x; 81 cury = ppos->o_y; 82 at = 1; 83 break; 84 case UP: 85 hvmode = U_DIR; 86 break; 87 case DOWN: 88 hvmode = D_DIR; 89 break; 90 case RIGHT: 91 hvmode = R_DIR; 92 break; 93 case LEFT: 94 hvmode = L_DIR; 95 break; 96 } 97 } 98 if (!at && !to) { /* the defaults are mostly OK */ 99 curx = fromx + prevrad * dctrx[cw][hvmode]; 100 cury = fromy + prevrad * dctry[cw][hvmode]; 101 tox = fromx + prevrad * dtox[cw][hvmode]; 102 toy = fromy + prevrad * dtoy[cw][hvmode]; 103 hvmode = nexthv[cw][hvmode]; 104 } 105 else if (!at) { 106 dx2 = (tox - fromx) / 2; 107 dy2 = (toy - fromy) / 2; 108 phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2); 109 if (prevrad <= 0.0) 110 prevrad = dx2*dx2+dy2*dy2; 111 for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2) 112 ; /* this kludge gets around too-small radii */ 113 prevrad = r; 114 ht = sqrt(d); 115 curx = fromx + dx2 + ht * cos(phi); 116 cury = fromy + dy2 + ht * sin(phi); 117 dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n", 118 dx2, dy2, phi, r, ht); 119 } 120 else if (at && !to) { /* do we have all the cases??? */ 121 tox = fromx + prevrad * dtox[cw][hvmode]; 122 toy = fromy + prevrad * dtoy[cw][hvmode]; 123 hvmode = nexthv[cw][hvmode]; 124 } 125 if (cw) { /* interchange roles of from-to and heads */ 126 float temp; 127 temp = fromx; fromx = tox; tox = temp; 128 temp = fromy; fromy = toy; toy = temp; 129 if (head == HEAD1) 130 head = HEAD2; 131 else if (head == HEAD2) 132 head = HEAD1; 133 } 134 p = makenode(type, 7); 135 arc_extreme(fromx, fromy, tox, toy, curx, cury); 136 p->o_val[0] = fromx; 137 p->o_val[1] = fromy; 138 p->o_val[2] = tox; 139 p->o_val[3] = toy; 140 if (cw) { 141 curx = fromx; 142 cury = fromy; 143 } else { 144 curx = tox; 145 cury = toy; 146 } 147 p->o_val[4] = prevw; 148 p->o_val[5] = prevh; 149 p->o_val[6] = prevrad; 150 p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype; 151 if (head) 152 p->o_nhead = getfval("arrowhead"); 153 dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n", 154 prevrad, p->o_x, p->o_y, 155 p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]); 156 return(p); 157 } 158 159 /*************************************************************************** 160 bounding box of a circular arc Eric Grosse 24 May 84 161 162 Conceptually, this routine generates a list consisting of the start, 163 end, and whichever north, east, south, and west points lie on the arc. 164 The bounding box is then the range of this list. 165 list = {start,end} 166 j = quadrant(start) 167 k = quadrant(end) 168 if( j==k && long way 'round ) append north,west,south,east 169 else 170 while( j != k ) 171 append center+radius*[j-th of north,west,south,east unit vectors] 172 j += 1 (mod 4) 173 return( bounding box of list ) 174 The following code implements this, with simple optimizations. 175 ***********************************************************************/ 176 177 178 arc_extreme(x0, y0, x1, y1, xc, yc) 179 double x0, y0, x1, y1, xc, yc; /* start, end, center */ 180 { 181 /* assumes center isn't too far out */ 182 double r, x, y, xmin, ymin, xmax, ymax; 183 int j, k; 184 x0 -= xc; y0 -= yc; /* move to center */ 185 x1 -= xc; y1 -= yc; 186 xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1; 187 xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1; 188 r = sqrt(x0*x0 + y0*y0); 189 if (r > 0.0) { 190 j = quadrant(x0,y0); 191 k = quadrant(x1,y1); 192 if (j == k && y1*x0 < x1*y0) { 193 /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */ 194 if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r; 195 if( xmax < r) xmax = r; if( ymax < r) ymax = r; 196 } else { 197 while (j != k) { 198 switch (j) { 199 case 1: if( ymax < r) ymax = r; break; /* north */ 200 case 2: if( xmin > -r) xmin = -r; break; /* west */ 201 case 3: if( ymin > -r) ymin = -r; break; /* south */ 202 case 4: if( xmax < r) xmax = r; break; /* east */ 203 } 204 j = j%4 + 1; 205 } 206 } 207 } 208 xmin += xc; ymin += yc; 209 xmax += xc; ymax += yc; 210 extreme(xmin, ymin); 211 extreme(xmax, ymax); 212 } 213 214 quadrant(x,y) 215 double x, y; 216 { 217 if ( x>=0.0 && y> 0.0) return(1); 218 else if( x< 0.0 && y>=0.0) return(2); 219 else if( x<=0.0 && y< 0.0) return(3); 220 else if( x> 0.0 && y<=0.0) return(4); 221 else 222 { fprintf(stderr,"can't happen: x,y=%g,%g",x,y); exit(1);} 223 } 224 225