xref: /plan9-contrib/sys/src/cmd/postscript/tr2post/draw.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <bio.h>
4219b2ee8SDavid du Colombier #include "../common/common.h"
5219b2ee8SDavid du Colombier #include "tr2post.h"
6219b2ee8SDavid du Colombier 
7219b2ee8SDavid du Colombier BOOLEAN drawflag = FALSE;
8219b2ee8SDavid du Colombier 
9219b2ee8SDavid du Colombier void
10219b2ee8SDavid du Colombier cover(double x, double y) {
11219b2ee8SDavid du Colombier }
12219b2ee8SDavid du Colombier 
13219b2ee8SDavid du Colombier void
14219b2ee8SDavid du Colombier drawspline(Biobufhdr *Bp, int flag) {	/* flag!=1 connect end points */
15219b2ee8SDavid du Colombier 	int x[100], y[100];
16219b2ee8SDavid du Colombier 	int i, N;
17219b2ee8SDavid du Colombier /*
18219b2ee8SDavid du Colombier  *
19219b2ee8SDavid du Colombier  * Spline drawing routine for Postscript printers. The complicated stuff is
20219b2ee8SDavid du Colombier  * handled by procedure Ds, which should be defined in the library file. I've
21219b2ee8SDavid du Colombier  * seen wrong implementations of troff's spline drawing, so fo the record I'll
22219b2ee8SDavid du Colombier  * write down the parametric equations and the necessary conversions to Bezier
23219b2ee8SDavid du Colombier  * cubic splines (as used in Postscript).
24219b2ee8SDavid du Colombier  *
25219b2ee8SDavid du Colombier  *
26219b2ee8SDavid du Colombier  * Parametric equation (x coordinate only):
27219b2ee8SDavid du Colombier  *
28219b2ee8SDavid du Colombier  *
29219b2ee8SDavid du Colombier  *	    (x2 - 2 * x1 + x0)    2                    (x0 + x1)
30219b2ee8SDavid du Colombier  *	x = ------------------ * t   + (x1 - x0) * t + ---------
31219b2ee8SDavid du Colombier  *		    2					   2
32219b2ee8SDavid du Colombier  *
33219b2ee8SDavid du Colombier  *
34219b2ee8SDavid du Colombier  * The coefficients in the Bezier cubic are,
35219b2ee8SDavid du Colombier  *
36219b2ee8SDavid du Colombier  *
37219b2ee8SDavid du Colombier  *	A = 0
38219b2ee8SDavid du Colombier  *	B = (x2 - 2 * x1 + x0) / 2
39219b2ee8SDavid du Colombier  *	C = x1 - x0
40219b2ee8SDavid du Colombier  *
41219b2ee8SDavid du Colombier  *
42219b2ee8SDavid du Colombier  * while the current point is,
43219b2ee8SDavid du Colombier  *
44219b2ee8SDavid du Colombier  *	current-point = (x0 + x1) / 2
45219b2ee8SDavid du Colombier  *
46219b2ee8SDavid du Colombier  * Using the relationships given in the Postscript manual (page 121) it's easy to
47219b2ee8SDavid du Colombier  * see that the control points are given by,
48219b2ee8SDavid du Colombier  *
49219b2ee8SDavid du Colombier  *
50219b2ee8SDavid du Colombier  *	x0' = (x0 + 5 * x1) / 6
51219b2ee8SDavid du Colombier  *	x1' = (x2 + 5 * x1) / 6
52219b2ee8SDavid du Colombier  *	x2' = (x1 + x2) / 2
53219b2ee8SDavid du Colombier  *
54219b2ee8SDavid du Colombier  *
55219b2ee8SDavid du Colombier  * where the primed variables are the ones used by curveto. The calculations
56219b2ee8SDavid du Colombier  * shown above are done in procedure Ds using the coordinates set up in both
57219b2ee8SDavid du Colombier  * the x[] and y[] arrays.
58219b2ee8SDavid du Colombier  *
59219b2ee8SDavid du Colombier  * A simple test of whether your spline drawing is correct would be to use cip
60219b2ee8SDavid du Colombier  * to draw a spline and some tangent lines at appropriate points and then print
61219b2ee8SDavid du Colombier  * the file.
62219b2ee8SDavid du Colombier  *
63219b2ee8SDavid du Colombier  */
64219b2ee8SDavid du Colombier 
65219b2ee8SDavid du Colombier 	for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
66219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
67219b2ee8SDavid du Colombier 			break;
68219b2ee8SDavid du Colombier 
69219b2ee8SDavid du Colombier 	x[0] = x[1] = hpos;
70219b2ee8SDavid du Colombier 	y[0] = y[1] = vpos;
71219b2ee8SDavid du Colombier 
72219b2ee8SDavid du Colombier 	for (i = 1; i < N; i++) {
73219b2ee8SDavid du Colombier 		x[i+1] += x[i];
74219b2ee8SDavid du Colombier 		y[i+1] += y[i];
75219b2ee8SDavid du Colombier 	}
76219b2ee8SDavid du Colombier 
77219b2ee8SDavid du Colombier 	x[N] = x[N-1];
78219b2ee8SDavid du Colombier 	y[N] = y[N-1];
79219b2ee8SDavid du Colombier 
80219b2ee8SDavid du Colombier 	for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
81219b2ee8SDavid du Colombier 		endstring();
82219b2ee8SDavid du Colombier 		if (pageon())
83219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
84219b2ee8SDavid du Colombier /*		if (dobbox == TRUE) {		/* could be better */
85219b2ee8SDavid du Colombier /*	    		cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
86219b2ee8SDavid du Colombier /*	    		cover((double)x[i+1], (double)-y[i+1]);
87219b2ee8SDavid du Colombier /*	    		cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
88219b2ee8SDavid du Colombier /*		}
89219b2ee8SDavid du Colombier  */
90219b2ee8SDavid du Colombier 	}
91219b2ee8SDavid du Colombier 
92219b2ee8SDavid du Colombier 	hpos = x[N];			/* where troff expects to be */
93219b2ee8SDavid du Colombier 	vpos = y[N];
94219b2ee8SDavid du Colombier }
95219b2ee8SDavid du Colombier 
96219b2ee8SDavid du Colombier void
97219b2ee8SDavid du Colombier draw(Biobufhdr *Bp) {
98219b2ee8SDavid du Colombier 
99219b2ee8SDavid du Colombier 	int r, x1, y1, x2, y2, i;
100219b2ee8SDavid du Colombier 	int d1, d2;
101219b2ee8SDavid du Colombier 
102219b2ee8SDavid du Colombier 	drawflag = TRUE;
103219b2ee8SDavid du Colombier 	r = Bgetrune(Bp);
104219b2ee8SDavid du Colombier 	switch(r) {
105219b2ee8SDavid du Colombier 	case 'l':
106219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
107219b2ee8SDavid du Colombier 			error(FATAL, "draw line function, destination coordinates not found.\n");
108219b2ee8SDavid du Colombier 
109219b2ee8SDavid du Colombier 		endstring();
110219b2ee8SDavid du Colombier 		if (pageon())
111219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
112219b2ee8SDavid du Colombier 		hpos += x1;
113219b2ee8SDavid du Colombier 		vpos += y1;
114219b2ee8SDavid du Colombier 		break;
115219b2ee8SDavid du Colombier 	case 'c':
116219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &d1, 0)<=0)
117219b2ee8SDavid du Colombier 			error(FATAL, "draw circle function, diameter coordinates not found.\n");
118219b2ee8SDavid du Colombier 
119219b2ee8SDavid du Colombier 		endstring();
120219b2ee8SDavid du Colombier 		if (pageon())
121219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
122219b2ee8SDavid du Colombier 		hpos += d1;
123219b2ee8SDavid du Colombier 		break;
124219b2ee8SDavid du Colombier 	case 'e':
125219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
126219b2ee8SDavid du Colombier 			error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
127219b2ee8SDavid du Colombier 
128219b2ee8SDavid du Colombier 		endstring();
129219b2ee8SDavid du Colombier 		if (pageon())
130219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
131219b2ee8SDavid du Colombier 		hpos += d1;
132219b2ee8SDavid du Colombier 		break;
133219b2ee8SDavid du Colombier 	case 'a':
134219b2ee8SDavid du Colombier 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'd', &x2, 0)<=0 || Bgetfield(Bp, 'd', &y2, 0)<=0)
135219b2ee8SDavid du Colombier 			error(FATAL, "draw arc function, coordinates not found.\n");
136219b2ee8SDavid du Colombier 
137219b2ee8SDavid du Colombier 		endstring();
138219b2ee8SDavid du Colombier 		if (pageon())
139219b2ee8SDavid du Colombier 			Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
140219b2ee8SDavid du Colombier 		hpos += x1 + x2;
141219b2ee8SDavid du Colombier 		vpos += y1 + y2;
142219b2ee8SDavid du Colombier 		break;
143219b2ee8SDavid du Colombier 	case 'q':
144219b2ee8SDavid du Colombier 		drawspline(Bp, 1);
145219b2ee8SDavid du Colombier 		break;
146219b2ee8SDavid du Colombier 	case '~':
147219b2ee8SDavid du Colombier 		drawspline(Bp, 2);
148219b2ee8SDavid du Colombier 		break;
149219b2ee8SDavid du Colombier 	default:
150219b2ee8SDavid du Colombier 		error(FATAL, "unknown draw function <%c>\n", r);
151219b2ee8SDavid du Colombier 		break;
152219b2ee8SDavid du Colombier 	}
153219b2ee8SDavid du Colombier }
154*7dd7cddfSDavid du Colombier 
155*7dd7cddfSDavid du Colombier #ifdef NOTYET
156*7dd7cddfSDavid du Colombier void
157*7dd7cddfSDavid du Colombier beginpath(Biobufhdr *Bp, int copy) {
158*7dd7cddfSDavid du Colombier 
159*7dd7cddfSDavid du Colombier /*
160*7dd7cddfSDavid du Colombier  * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
161*7dd7cddfSDavid du Colombier  * to mark the start of a sequence of drawing commands that should be grouped
162*7dd7cddfSDavid du Colombier  * together and treated as a single path. By default the drawing procedures in
163*7dd7cddfSDavid du Colombier  * *drawfile treat each drawing command as a separate object, and usually start
164*7dd7cddfSDavid du Colombier  * with a newpath (just as a precaution) and end with a stroke. The newpath and
165*7dd7cddfSDavid du Colombier  * stroke isolate individual drawing commands and make it impossible to deal with
166*7dd7cddfSDavid du Colombier  * composite objects. "x X BeginPath" can be used to mark the start of drawing
167*7dd7cddfSDavid du Colombier  * commands that should be grouped together and treated as a single object, and
168*7dd7cddfSDavid du Colombier  * part of what's done here ensures that the PostScript drawing commands defined
169*7dd7cddfSDavid du Colombier  * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
170*7dd7cddfSDavid du Colombier  * command. At that point the path that's been built up can be manipulated in
171*7dd7cddfSDavid du Colombier  * various ways (eg. filled and/or stroked with a different line width).
172*7dd7cddfSDavid du Colombier  *
173*7dd7cddfSDavid du Colombier  * Color selection is one of the options that's available in parsebuf(),
174*7dd7cddfSDavid du Colombier  * so if we get here we add *colorfile to the output file before doing
175*7dd7cddfSDavid du Colombier  * anything important.
176*7dd7cddfSDavid du Colombier  *
177*7dd7cddfSDavid du Colombier  */
178*7dd7cddfSDavid du Colombier 
179*7dd7cddfSDavid du Colombier 	if (inpath == FALSE) {
180*7dd7cddfSDavid du Colombier 		endstring();
181*7dd7cddfSDavid du Colombier 		getdraw();
182*7dd7cddfSDavid du Colombier 		getcolor();
183*7dd7cddfSDavid du Colombier 		Bprint(Bstdout, "gsave\n");
184*7dd7cddfSDavid du Colombier 		Bprint(Bstdout, "newpath\n");
185*7dd7cddfSDavid du Colombier 		Bprint(Bstdout, "%d %d m\n", hpos, vpos);
186*7dd7cddfSDavid du Colombier 		Bprint(Bstdout, "/inpath true def\n");
187*7dd7cddfSDavid du Colombier 		if ( copy == TRUE )
188*7dd7cddfSDavid du Colombier 			fprintf(tf, "%s", buf);
189*7dd7cddfSDavid du Colombier 		inpath = TRUE;
190*7dd7cddfSDavid du Colombier 	}
191*7dd7cddfSDavid du Colombier }
192*7dd7cddfSDavid du Colombier #endif
193