xref: /plan9/sys/src/cmd/pic/linegen.c (revision 392dc26962a4986819d746514f28dfe13b47a3d3)
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include "pic.h"
6 #include "y.tab.h"
7 
linegen(int type)8 obj *linegen(int type)
9 {
10 	static double prevdx = HT;
11 	static double prevdy = 0;
12 	static double prevw = HT10;
13 	static double prevh = HT5;
14 	int i, j, some, head, ddtype, invis, chop, battr, with;
15 	double ddval, chop1, chop2, x0, y0, x1, y1;
16 	double fillval = 0;
17 	double theta;
18 	double defx, defy, xwith, ywith;
19 	obj *p, *ppos;
20 	static int xtab[] = { 1, 0, -1, 0 };	/* R=0, U=1, L=2, D=3 */
21 	static int ytab[] = { 0, 1, 0, -1 };
22 	double dx[500], dy[500];
23 	int ndxy;
24 	double nx, ny;
25 	Attr *ap, *chop_ap[4];
26 
27 	nx = curx;
28 	ny = cury;
29 	defx = getfval("linewid");
30 	defy = getfval("lineht");
31 	prevh = getfval("arrowht");
32 	prevw = getfval("arrowwid");
33 	dx[0] = dy[0] = ndxy = some = head = invis = battr = with = 0;
34 	chop = chop1 = chop2 = 0;
35 	ddtype = ddval = xwith = ywith = 0;
36 	for (i = 0; i < nattr; i++) {
37 		ap = &attr[i];
38 		switch (ap->a_type) {
39 		case TEXTATTR:
40 			savetext(ap->a_sub, ap->a_val.p);
41 			break;
42 		case HEAD:
43 			head += ap->a_val.i;
44 			break;
45 		case INVIS:
46 			invis = INVIS;
47 			break;
48 		case NOEDGE:
49 			battr |= NOEDGEBIT;
50 			break;
51 		case DOT:
52 		case DASH:
53 			ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT;
54 			if (ap->a_sub == DEFAULT)
55 				ddval = getfval("dashwid");
56 			else
57 				ddval = ap->a_val.f;
58 			break;
59 		case SAME:
60 			dx[ndxy] = prevdx;
61 			dy[ndxy] = prevdy;
62 			some++;
63 			break;
64 		case LEFT:
65 			dx[ndxy] -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
66 			some++;
67 			hvmode = L_DIR;
68 			break;
69 		case RIGHT:
70 			dx[ndxy] += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
71 			some++;
72 			hvmode = R_DIR;
73 			break;
74 		case UP:
75 			dy[ndxy] += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
76 			some++;
77 			hvmode = U_DIR;
78 			break;
79 		case DOWN:
80 			dy[ndxy] -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
81 			some++;
82 			hvmode = D_DIR;
83 			break;
84 		case HEIGHT:	/* length of arrowhead */
85 			prevh = ap->a_val.f;
86 			break;
87 		case WIDTH:	/* width of arrowhead */
88 			prevw = ap->a_val.f;
89 			break;
90 		case TO:
91 			if (some) {
92 				nx += dx[ndxy];
93 				ny += dy[ndxy];
94 				ndxy++;
95 				dx[ndxy] = dy[ndxy] = some = 0;
96 			}
97 			ppos = attr[i].a_val.o;
98 			if (ppos == NULL)
99 				ERROR "no tag defined for `to'" FATAL;
100 			dx[ndxy] = ppos->o_x - nx;
101 			dy[ndxy] = ppos->o_y - ny;
102 			some++;
103 			break;
104 		case BY:
105 			if (some) {
106 				nx += dx[ndxy];
107 				ny += dy[ndxy];
108 				ndxy++;
109 				dx[ndxy] = dy[ndxy] = some = 0;
110 			}
111 			ppos = ap->a_val.o;
112 			if (ppos == NULL)
113 				ERROR "no tag defined for `by'" FATAL;
114 			dx[ndxy] = ppos->o_x;
115 			dy[ndxy] = ppos->o_y;
116 			some++;
117 			break;
118 		case THEN:	/* turn off any previous accumulation */
119 			if (some) {
120 				nx += dx[ndxy];
121 				ny += dy[ndxy];
122 				ndxy++;
123 				dx[ndxy] = dy[ndxy] = some = 0;
124 			}
125 			break;
126 		case FROM:
127 		case AT:
128 			ppos = ap->a_val.o;
129 			if (ppos == NULL)
130 				ERROR "no tag defined for `from' or `at'" FATAL;
131 			nx = curx = ppos->o_x;
132 			ny = cury = ppos->o_y;
133 			break;
134 		case WITH:
135 			with = ap->a_val.i;
136 			break;
137 		case CHOP:
138 			if (ap->a_sub != PLACENAME) {
139 				if( chop == 0)
140 					chop1 = chop2 = ap->a_val.f;
141 				else
142 					chop2 = ap->a_val.f;
143 			}
144 			chop_ap[chop++] = ap;
145 			break;
146 		case FILL:
147 			battr |= FILLBIT;
148 			if (ap->a_sub == DEFAULT)
149 				fillval = getfval("fillval");
150 			else
151 				fillval = ap->a_val.f;
152 			break;
153 		}
154 	}
155 	if (with) {	/* this doesn't work at all */
156 		switch (with) {
157 		case CENTER:
158 			xwith = (dx[1] - dx[0]) / 2; ywith = (dy[1] - dy[0]) / 2; break;
159 		}
160 		for (i = 0; i < ndxy; i++) {
161 			dx[i] -= xwith;
162 			dy[i] -= ywith;
163 		}
164 		curx += xwith;
165 		cury += ywith;
166 	}
167 	if (some) {
168 		nx += dx[ndxy];
169 		ny += dy[ndxy];
170 		ndxy++;
171 		defx = dx[ndxy-1];
172 		defy = dy[ndxy-1];
173 	} else {
174 		defx *= xtab[hvmode];
175 		defy *= ytab[hvmode];
176 		dx[ndxy] = defx;
177 		dy[ndxy] = defy;
178 		ndxy++;
179 		nx += defx;
180 		ny += defy;
181 	}
182 	prevdx = defx;
183 	prevdy = defy;
184 	if (chop) {
185 		if (chop == 1 && chop1 == 0)	/* just said "chop", so use default */
186 			chop1 = chop2 = getfval("circlerad");
187 		theta = atan2(dy[0], dx[0]);
188 		x0 = chop1 * cos(theta);
189 		y0 = chop1 * sin(theta);
190 		curx += x0;
191 		cury += y0;
192 		dx[0] -= x0;
193 		dy[0] -= y0;
194 
195 		theta = atan2(dy[ndxy-1], dx[ndxy-1]);
196 		x1 = chop2 * cos(theta);
197 		y1 = chop2 * sin(theta);
198 		nx -= x1;
199 		ny -= y1;
200 		dx[ndxy-1] -= x1;
201 		dy[ndxy-1] -= y1;
202 		dprintf("chopping %g %g %g %g; cur=%g,%g end=%g,%g\n",
203 			x0, y0, x1, y1, curx, cury, nx, ny);
204 	}
205 	p = makenode(type, 5 + 2 * ndxy);
206 	curx = p->o_val[0] = nx;
207 	cury = p->o_val[1] = ny;
208 	if (head || type == ARROW) {
209 		p->o_nhead = getfval("arrowhead");
210 		p->o_val[2] = prevw;
211 		p->o_val[3] = prevh;
212 		if (head == 0)
213 			head = HEAD2;	/* default arrow head */
214 	}
215 	p->o_attr = head | invis | ddtype | battr;
216 	p->o_fillval = fillval;
217 	p->o_val[4] = ndxy;
218 	nx = p->o_x;
219 	ny = p->o_y;
220 	for (i = 0, j = 5; i < ndxy; i++, j += 2) {
221 		p->o_val[j] = dx[i];
222 		p->o_val[j+1] = dy[i];
223 		if (type == LINE || type == ARROW)
224 			extreme(nx += dx[i], ny += dy[i]);
225 		else if (type == SPLINE && i < ndxy-1) {
226 			/* to compute approx extreme of spline at p,
227 			/* compute midway between p-1 and p+1,
228 			/* then go 3/4 from there to p */
229 			double ex, ey, xi, yi, xi1, yi1;
230 			xi = nx + dx[i]; yi = ny + dy[i];	/* p */
231 			xi1 = xi + dx[i+1]; yi1 = yi + dy[i+1];	/* p+1 */
232 			ex = (nx+xi1)/2; ey = (ny+yi1)/2;	/* midway */
233 			ex += 0.75*(xi-ex); ey += 0.75*(yi-ey);
234 			extreme(ex, ey);
235 			nx = xi; ny = yi;
236 		}
237 
238 	}
239 	p->o_ddval = ddval;
240 	if (dbg) {
241 		printf("S or L from %g %g to %g %g with %d elements:\n", p->o_x, p->o_y, curx, cury, ndxy);
242 		for (i = 0, j = 5; i < ndxy; i++, j += 2)
243 			printf("%g %g\n", p->o_val[j], p->o_val[j+1]);
244 	}
245 	extreme(p->o_x, p->o_y);
246 	extreme(curx, cury);
247 	return(p);
248 }
249