xref: /csrg-svn/usr.bin/plot/crtplot.c (revision 15505)
1*15505Sralph #ifndef lint
2*15505Sralph static char sccsid[] = "@(#)crtplot.c	4.1 (Berkeley) 11/11/83";
3*15505Sralph #endif
4*15505Sralph 
5*15505Sralph /*
6*15505Sralph This plotting routine interprets plot commands and outputs them onto
7*15505Sralph intelligent terminals (ie, terminals with clear screen and cursor
8*15505Sralph addressability.  It uses the curses library.  It should be compiled
9*15505Sralph as follows:
10*15505Sralph 	cc crtdriver.c crtplot.c -lcurses -ltermcap -lm
11*15505Sralph Note:  This requires as slightly modified driver from the standard driver
12*15505Sralph because some function names conflicted with the curses library.
13*15505Sralph (That's what you get when you have a flat name space!)
14*15505Sralph */
15*15505Sralph 
16*15505Sralph 
17*15505Sralph #include <curses.h>
18*15505Sralph #include <math.h>
19*15505Sralph #include <signal.h>
20*15505Sralph 
21*15505Sralph 
22*15505Sralph /*  These map from plot routine coordinates to screen coordinates.  */
23*15505Sralph #define scaleX(x)		(int) ((x-lowX)*rangeX + 0.5)
24*15505Sralph #define scaleY(y)		(int) (LINES-0.5 - ((y-lowY)*rangeY))
25*15505Sralph 
26*15505Sralph #define plot_movech(y, x, ch)	{ plot_move(x, y); plot_addch(ch); }
27*15505Sralph 
28*15505Sralph 
29*15505Sralph static double lowX, rangeX;	/* min and range of x */
30*15505Sralph static double lowY, rangeY;	/* min and range of y */
31*15505Sralph static int lastX, lastY;	/* last point plotted */
32*15505Sralph 
33*15505Sralph 
34*15505Sralph char *getenv();
35*15505Sralph extern char _putchar();
36*15505Sralph 
37*15505Sralph /* This routine just moves the cursor. */
38*15505Sralph screen_move(y, x)
39*15505Sralph int x,y;
40*15505Sralph {
41*15505Sralph 	/* must check for automatic wrap at last col */
42*15505Sralph 	if (!AM || (y < LINES -1) || (x < COLS -1)) {
43*15505Sralph 		mvcur(lastY, lastX, y, x);
44*15505Sralph 		lastY = y;
45*15505Sralph 		lastX = x;
46*15505Sralph 		}
47*15505Sralph }
48*15505Sralph 
49*15505Sralph 
50*15505Sralph /* This routine assumes the cursor is positioned correctly. */
51*15505Sralph plot_addch(ch)
52*15505Sralph char ch;
53*15505Sralph {
54*15505Sralph 	putchar(ch);
55*15505Sralph 	if (++lastX >= COLS) {
56*15505Sralph 		if (AM) {
57*15505Sralph 			lastX = 0;
58*15505Sralph 			lastY++;
59*15505Sralph 		} else {
60*15505Sralph 			lastX = COLS - 1;
61*15505Sralph 			}
62*15505Sralph 		}
63*15505Sralph }
64*15505Sralph 
65*15505Sralph 
66*15505Sralph 
67*15505Sralph 
68*15505Sralph /* See the curses manual for what is been done and why. */
69*15505Sralph openpl()
70*15505Sralph {
71*15505Sralph char *sp;
72*15505Sralph int closepl();
73*15505Sralph 
74*15505Sralph gettmode();
75*15505Sralph if (sp=getenv("TERM"))
76*15505Sralph 	setterm(sp);
77*15505Sralph signal(SIGINT, closepl);
78*15505Sralph 
79*15505Sralph }
80*15505Sralph 
81*15505Sralph 
82*15505Sralph 
83*15505Sralph 
84*15505Sralph closepl()
85*15505Sralph {
86*15505Sralph signal(SIGINT, SIG_IGN);
87*15505Sralph /* Leave cursor at top of screen. */
88*15505Sralph mvcur(LINES-1, COLS-1, 0, 0);
89*15505Sralph endwin();
90*15505Sralph exit(0);
91*15505Sralph }
92*15505Sralph 
93*15505Sralph 
94*15505Sralph 
95*15505Sralph plot_move(x,y)
96*15505Sralph int x, y;
97*15505Sralph {
98*15505Sralph screen_move(scaleY(y), scaleX(x));
99*15505Sralph }
100*15505Sralph 
101*15505Sralph 
102*15505Sralph 
103*15505Sralph line(x0, y0, x1, y1)
104*15505Sralph int x0, y0, x1, y1;
105*15505Sralph {
106*15505Sralph plot_movech(y0, x0, '*');
107*15505Sralph dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
108*15505Sralph }
109*15505Sralph 
110*15505Sralph label(str)
111*15505Sralph char *str;
112*15505Sralph {
113*15505Sralph 	reg i, length;
114*15505Sralph 	int strlen();
115*15505Sralph 
116*15505Sralph 	if ( (length=strlen(str)) > (COLS-lastX) )
117*15505Sralph 		length = COLS - lastX;
118*15505Sralph 	for (i=0; i<length; ++i)
119*15505Sralph 		plot_addch(str[i]);
120*15505Sralph }
121*15505Sralph 
122*15505Sralph plot_erase()
123*15505Sralph {
124*15505Sralph /*
125*15505Sralph Some of these functions probably belong in openpl().  However, if the
126*15505Sralph input is being typed in, putting them in openpl would not work
127*15505Sralph since "noecho", etc would prevent (sort of) input.  Notice that
128*15505Sralph the driver calls openpl before doing anything.  This is actually
129*15505Sralph wrong, but it is what whoever originally wrote the driver decided
130*15505Sralph to do.  (openpl() in libplot does nothing -- that is the main problem!)
131*15505Sralph */
132*15505Sralph _puts(TI);
133*15505Sralph _puts(VS);
134*15505Sralph 
135*15505Sralph noecho();
136*15505Sralph nonl();
137*15505Sralph tputs(CL, LINES, _putchar);
138*15505Sralph mvcur(0, COLS-1, LINES-1, 0);
139*15505Sralph lastX = 0;
140*15505Sralph lastY = LINES-1;
141*15505Sralph }
142*15505Sralph 
143*15505Sralph 
144*15505Sralph point(x, y)
145*15505Sralph int x,y;
146*15505Sralph {
147*15505Sralph plot_movech(y, x, '*');
148*15505Sralph }
149*15505Sralph 
150*15505Sralph 
151*15505Sralph cont(x, y)
152*15505Sralph int x,y;
153*15505Sralph {
154*15505Sralph dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
155*15505Sralph }
156*15505Sralph 
157*15505Sralph 
158*15505Sralph space(x0, y0, x1, y1)
159*15505Sralph int x0, y0, x1, y1;
160*15505Sralph {
161*15505Sralph lowX = (double) x0;
162*15505Sralph lowY = (double) y0;
163*15505Sralph rangeX = COLS/(double) (x1 - x0);
164*15505Sralph rangeY = LINES/(double) (y1 - y0);
165*15505Sralph }
166*15505Sralph 
167*15505Sralph 
168*15505Sralph linemod(string)
169*15505Sralph char *string;
170*15505Sralph {
171*15505Sralph }
172*15505Sralph 
173*15505Sralph 
174*15505Sralph 
175*15505Sralph /* See Neuman & Sproul for explanation and rationale. */
176*15505Sralph /* Does not plot first point -- assumed that it is already plotted */
177*15505Sralph dda_line(ch, x0, y0, x1, y1)
178*15505Sralph char ch;
179*15505Sralph int x0, y0, x1, y1;	/* already transformed to screen coords */
180*15505Sralph {
181*15505Sralph 	int length, i;
182*15505Sralph 	double deltaX, deltaY;
183*15505Sralph 	double x, y;
184*15505Sralph 	double floor();
185*15505Sralph 	int abs();
186*15505Sralph 
187*15505Sralph length = abs(x1 - x0);
188*15505Sralph if (abs(y1 -y0) > length)
189*15505Sralph 	length = abs(y1 - y0);
190*15505Sralph 
191*15505Sralph if (length == 0)
192*15505Sralph 	return;
193*15505Sralph 
194*15505Sralph deltaX = (double) (x1 - x0)/(double) length;
195*15505Sralph deltaY = (double) (y1 - y0)/(double) length;
196*15505Sralph 
197*15505Sralph x = (double) x0 + 0.5;
198*15505Sralph y = (double) y0 + 0.5;
199*15505Sralph 
200*15505Sralph for (i=0; i < length; ++i)
201*15505Sralph 	{
202*15505Sralph 	x += deltaX;
203*15505Sralph 	y += deltaY;
204*15505Sralph 	screen_move((int) floor(y), (int) floor(x));
205*15505Sralph 	plot_addch(ch);
206*15505Sralph 	}
207*15505Sralph }
208*15505Sralph 
209*15505Sralph 
210*15505Sralph circle (xc,yc,r)
211*15505Sralph int xc,yc,r;
212*15505Sralph {
213*15505Sralph 	arc(xc,yc, xc+r,yc, xc-r,yc);
214*15505Sralph 	arc(xc,yc, xc-r,yc, xc+r,yc);
215*15505Sralph }
216*15505Sralph 
217*15505Sralph 
218*15505Sralph /* should include test for equality? */
219*15505Sralph #define side(x,y)	(a*(x)+b*(y)+c > 0.0 ? 1 : -1)
220*15505Sralph 
221*15505Sralph arc(xc,yc,xbeg,ybeg,xend,yend)
222*15505Sralph int xc,yc,xbeg,ybeg,xend,yend;
223*15505Sralph {
224*15505Sralph 	double r, radius, costheta, sintheta;
225*15505Sralph 	double a, b, c, x, y, tempX;
226*15505Sralph 	int right_side;
227*15505Sralph 
228*15505Sralph 	xbeg -= xc; ybeg -= yc;
229*15505Sralph 	xend -= xc; yend -= yc;
230*15505Sralph 
231*15505Sralph 	/* probably should check that arc is truely circular */
232*15505Sralph 	/* Note: r is in screen coordinates. */
233*15505Sralph 	r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg);
234*15505Sralph 
235*15505Sralph 	/*
236*15505Sralph 	This method is reasonably efficient, clean, and clever.
237*15505Sralph 	The easy part is generating the next point on the arc.  This is
238*15505Sralph 	done by rotating the points by the angle theta.  Theta is chosen
239*15505Sralph 	so that no rotation will cause more than one pixel of a move.
240*15505Sralph 	This corresponds to a triangle having 'x side' of r and 'y side' of 1.
241*15505Sralph 	The rotation is done (way) below inside the loop.
242*15505Sralph 	*/
243*15505Sralph 	if (r <= 1.0) {
244*15505Sralph 		/* radius is mapped to length < 1*/
245*15505Sralph 		point(xc,yc);
246*15505Sralph 		return;
247*15505Sralph 		}
248*15505Sralph 
249*15505Sralph 	radius = sqrt(r*r + 1.0);
250*15505Sralph 	sintheta = 1.0/radius;
251*15505Sralph 	costheta = r/radius;
252*15505Sralph 
253*15505Sralph 	/*
254*15505Sralph 	The hard part of drawing an arc is figuring out when to stop.
255*15505Sralph 	This method works by drawing the line from the beginning point
256*15505Sralph 	to the ending point.  This splits the plane in half, with the
257*15505Sralph 	arc that we wish to draw on one side of the line.  If we evaluate
258*15505Sralph 	side(x,y) = a*x + b*y + c, then all of the points on one side of the
259*15505Sralph 	line will result in side being positive, and all the points on the
260*15505Sralph 	other side of the line will result in side being negative.
261*15505Sralph 
262*15505Sralph 	We want to draw the arc in a counter-clockwise direction, so we
263*15505Sralph 	must find out what the sign of "side" is for a point which is to the
264*15505Sralph 	"right" of a line drawn from "beg" to "end".  A point which must lie
265*15505Sralph 	on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)].  (This
266*15505Sralph 	point is perpendicular to the line at "beg").
267*15505Sralph 
268*15505Sralph 	Thus, we compute "side" of the above point, and then compare the
269*15505Sralph 	sign of side for each new point with the sign of the above point.
270*15505Sralph 	When they are different, we terminate the loop.
271*15505Sralph 	*/
272*15505Sralph 
273*15505Sralph 	a = (double) (yend - ybeg);
274*15505Sralph 	b = (double) (xend - xbeg);
275*15505Sralph 	c = (double) (yend*xbeg - xend*ybeg);
276*15505Sralph 	right_side = side(xbeg + (yend-ybeg),
277*15505Sralph 			  ybeg - (xend-xbeg) );
278*15505Sralph 
279*15505Sralph 	x = xbeg;
280*15505Sralph 	y = ybeg;
281*15505Sralph 	plot_move(xbeg+xc, ybeg+yc);
282*15505Sralph 	do {
283*15505Sralph 		dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y ));
284*15505Sralph 		/*
285*15505Sralph 		screen_move( scaleY(yc + y), scaleX(xc + x) );
286*15505Sralph 		plot_addch('*');
287*15505Sralph 		*/
288*15505Sralph 		tempX = x;
289*15505Sralph 		x = x*costheta - y*sintheta;
290*15505Sralph 		y = tempX*sintheta + y*costheta;
291*15505Sralph 	} while( side(x,y) == right_side );
292*15505Sralph }
293