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