Blob


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