xref: /original-bsd/usr.bin/plot/crtplot.c (revision 37071c60)
1655593d0Sbostic /*-
2655593d0Sbostic  * %sccs.include.proprietary.c%
3655593d0Sbostic  */
4655593d0Sbostic 
5956c6c94Sralph #ifndef lint
6*37071c60Sbostic static char sccsid[] = "@(#)crtplot.c	4.7 (Berkeley) 01/07/94";
7655593d0Sbostic #endif /* not lint */
8956c6c94Sralph 
9956c6c94Sralph /*
10956c6c94Sralph This plotting routine interprets plot commands and outputs them onto
11956c6c94Sralph intelligent terminals (ie, terminals with clear screen and cursor
12956c6c94Sralph addressability.  It uses the curses library.  It should be compiled
13956c6c94Sralph as follows:
14956c6c94Sralph 	cc crtdriver.c crtplot.c -lcurses -ltermcap -lm
15956c6c94Sralph Note:  This requires as slightly modified driver from the standard driver
16956c6c94Sralph because some function names conflicted with the curses library.
17956c6c94Sralph (That's what you get when you have a flat name space!)
18956c6c94Sralph */
19956c6c94Sralph 
20956c6c94Sralph 
21956c6c94Sralph #include <curses.h>
22956c6c94Sralph #include <math.h>
23956c6c94Sralph #include <signal.h>
246f136989Storek #include <stdlib.h>
256f136989Storek #include <string.h>
26956c6c94Sralph 
27956c6c94Sralph 
28956c6c94Sralph /*  These map from plot routine coordinates to screen coordinates.  */
29956c6c94Sralph #define scaleX(x)		(int) ((x-lowX)*rangeX + 0.5)
30956c6c94Sralph #define scaleY(y)		(int) (LINES-0.5 - ((y-lowY)*rangeY))
31956c6c94Sralph 
32956c6c94Sralph #define plot_movech(y, x, ch)	{ plot_move(x, y); plot_addch(ch); }
33956c6c94Sralph 
34956c6c94Sralph 
35956c6c94Sralph static double lowX, rangeX;	/* min and range of x */
36956c6c94Sralph static double lowY, rangeY;	/* min and range of y */
37956c6c94Sralph static int lastX, lastY;	/* last point plotted */
38956c6c94Sralph 
39956c6c94Sralph 
40956c6c94Sralph /* This routine just moves the cursor. */
screen_move(y,x)41956c6c94Sralph screen_move(y, x)
42956c6c94Sralph int x,y;
43956c6c94Sralph {
44956c6c94Sralph 	/* must check for automatic wrap at last col */
45956c6c94Sralph 	if (!AM || (y < LINES -1) || (x < COLS -1)) {
46956c6c94Sralph 		mvcur(lastY, lastX, y, x);
47956c6c94Sralph 		lastY = y;
48956c6c94Sralph 		lastX = x;
49956c6c94Sralph 		}
50956c6c94Sralph }
51956c6c94Sralph 
52956c6c94Sralph 
53956c6c94Sralph /* This routine assumes the cursor is positioned correctly. */
plot_addch(ch)54956c6c94Sralph plot_addch(ch)
55956c6c94Sralph char ch;
56956c6c94Sralph {
5763b2a11cSelan 	_putchar(ch);
58956c6c94Sralph 	if (++lastX >= COLS) {
59956c6c94Sralph 		if (AM) {
60956c6c94Sralph 			lastX = 0;
61956c6c94Sralph 			lastY++;
62956c6c94Sralph 		} else {
63956c6c94Sralph 			lastX = COLS - 1;
64956c6c94Sralph 			}
65956c6c94Sralph 		}
66956c6c94Sralph }
67956c6c94Sralph 
68956c6c94Sralph 
69956c6c94Sralph 
70956c6c94Sralph 
71956c6c94Sralph /* See the curses manual for what is been done and why. */
openpl()72956c6c94Sralph openpl()
73956c6c94Sralph {
74956c6c94Sralph char *sp;
7574d2773bSbostic void closepl();
76956c6c94Sralph 
77956c6c94Sralph gettmode();
78956c6c94Sralph if (sp=getenv("TERM"))
79956c6c94Sralph 	setterm(sp);
80956c6c94Sralph signal(SIGINT, closepl);
81956c6c94Sralph 
82956c6c94Sralph }
83956c6c94Sralph 
84956c6c94Sralph 
85956c6c94Sralph 
86956c6c94Sralph 
8774d2773bSbostic void
closepl()88956c6c94Sralph closepl()
89956c6c94Sralph {
90956c6c94Sralph signal(SIGINT, SIG_IGN);
91956c6c94Sralph /* Leave cursor at top of screen. */
92956c6c94Sralph mvcur(LINES-1, COLS-1, 0, 0);
93956c6c94Sralph endwin();
94956c6c94Sralph exit(0);
95956c6c94Sralph }
96956c6c94Sralph 
97956c6c94Sralph 
98956c6c94Sralph 
plot_move(x,y)99956c6c94Sralph plot_move(x,y)
100956c6c94Sralph int x, y;
101956c6c94Sralph {
102956c6c94Sralph screen_move(scaleY(y), scaleX(x));
103956c6c94Sralph }
104956c6c94Sralph 
105956c6c94Sralph 
106956c6c94Sralph 
line(x0,y0,x1,y1)107956c6c94Sralph line(x0, y0, x1, y1)
108956c6c94Sralph int x0, y0, x1, y1;
109956c6c94Sralph {
110956c6c94Sralph plot_movech(y0, x0, '*');
111956c6c94Sralph dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
112956c6c94Sralph }
113956c6c94Sralph 
label(str)114956c6c94Sralph label(str)
115956c6c94Sralph char *str;
116956c6c94Sralph {
117956c6c94Sralph 	reg i, length;
118956c6c94Sralph 
119956c6c94Sralph 	if ( (length=strlen(str)) > (COLS-lastX) )
120956c6c94Sralph 		length = COLS - lastX;
121956c6c94Sralph 	for (i=0; i<length; ++i)
122956c6c94Sralph 		plot_addch(str[i]);
123956c6c94Sralph }
124956c6c94Sralph 
125eb9b57b3Sbostic void
cputchar(ch)126eb9b57b3Sbostic cputchar(ch)
127eb9b57b3Sbostic         int ch;
128eb9b57b3Sbostic {
129eb9b57b3Sbostic         (void)putchar(ch);
130eb9b57b3Sbostic }
131eb9b57b3Sbostic 
plot_erase()132956c6c94Sralph plot_erase()
133956c6c94Sralph {
134956c6c94Sralph /*
135956c6c94Sralph Some of these functions probably belong in openpl().  However, if the
136956c6c94Sralph input is being typed in, putting them in openpl would not work
137956c6c94Sralph since "noecho", etc would prevent (sort of) input.  Notice that
138956c6c94Sralph the driver calls openpl before doing anything.  This is actually
139956c6c94Sralph wrong, but it is what whoever originally wrote the driver decided
140956c6c94Sralph to do.  (openpl() in libplot does nothing -- that is the main problem!)
141956c6c94Sralph */
142eb9b57b3Sbostic tputs(TI, 0, cputchar);
143eb9b57b3Sbostic tputs(VS, 0, cputchar);
144956c6c94Sralph 
145956c6c94Sralph noecho();
146956c6c94Sralph nonl();
147eb9b57b3Sbostic tputs(CL, LINES, cputchar);
148956c6c94Sralph mvcur(0, COLS-1, LINES-1, 0);
149956c6c94Sralph lastX = 0;
150956c6c94Sralph lastY = LINES-1;
151956c6c94Sralph }
152956c6c94Sralph 
point(x,y)153956c6c94Sralph point(x, y)
154956c6c94Sralph int x,y;
155956c6c94Sralph {
156956c6c94Sralph plot_movech(y, x, '*');
157956c6c94Sralph }
158956c6c94Sralph 
159956c6c94Sralph 
cont(x,y)160956c6c94Sralph cont(x, y)
161956c6c94Sralph int x,y;
162956c6c94Sralph {
163956c6c94Sralph dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
164956c6c94Sralph }
165956c6c94Sralph 
166956c6c94Sralph 
space(x0,y0,x1,y1)167956c6c94Sralph space(x0, y0, x1, y1)
168956c6c94Sralph int x0, y0, x1, y1;
169956c6c94Sralph {
170956c6c94Sralph lowX = (double) x0;
171956c6c94Sralph lowY = (double) y0;
172956c6c94Sralph rangeX = COLS/(double) (x1 - x0);
173956c6c94Sralph rangeY = LINES/(double) (y1 - y0);
174956c6c94Sralph }
175956c6c94Sralph 
176956c6c94Sralph 
linemod(string)177956c6c94Sralph linemod(string)
178956c6c94Sralph char *string;
179956c6c94Sralph {
180956c6c94Sralph }
181956c6c94Sralph 
182956c6c94Sralph 
183956c6c94Sralph 
184956c6c94Sralph /* See Neuman & Sproul for explanation and rationale. */
185956c6c94Sralph /* Does not plot first point -- assumed that it is already plotted */
dda_line(ch,x0,y0,x1,y1)186956c6c94Sralph dda_line(ch, x0, y0, x1, y1)
187956c6c94Sralph char ch;
188956c6c94Sralph int x0, y0, x1, y1;	/* already transformed to screen coords */
189956c6c94Sralph {
190956c6c94Sralph 	int length, i;
191956c6c94Sralph 	double deltaX, deltaY;
192956c6c94Sralph 	double x, y;
193956c6c94Sralph 
194956c6c94Sralph length = abs(x1 - x0);
195956c6c94Sralph if (abs(y1 -y0) > length)
196956c6c94Sralph 	length = abs(y1 - y0);
197956c6c94Sralph 
198956c6c94Sralph if (length == 0)
199956c6c94Sralph 	return;
200956c6c94Sralph 
201956c6c94Sralph deltaX = (double) (x1 - x0)/(double) length;
202956c6c94Sralph deltaY = (double) (y1 - y0)/(double) length;
203956c6c94Sralph 
204956c6c94Sralph x = (double) x0 + 0.5;
205956c6c94Sralph y = (double) y0 + 0.5;
206956c6c94Sralph 
207956c6c94Sralph for (i=0; i < length; ++i)
208956c6c94Sralph 	{
209956c6c94Sralph 	x += deltaX;
210956c6c94Sralph 	y += deltaY;
211956c6c94Sralph 	screen_move((int) floor(y), (int) floor(x));
212956c6c94Sralph 	plot_addch(ch);
213956c6c94Sralph 	}
214956c6c94Sralph }
215956c6c94Sralph 
216956c6c94Sralph 
circle(xc,yc,r)217956c6c94Sralph circle (xc,yc,r)
218956c6c94Sralph int xc,yc,r;
219956c6c94Sralph {
220956c6c94Sralph 	arc(xc,yc, xc+r,yc, xc-r,yc);
221956c6c94Sralph 	arc(xc,yc, xc-r,yc, xc+r,yc);
222956c6c94Sralph }
223956c6c94Sralph 
224956c6c94Sralph 
225956c6c94Sralph /* should include test for equality? */
226956c6c94Sralph #define side(x,y)	(a*(x)+b*(y)+c > 0.0 ? 1 : -1)
227956c6c94Sralph 
arc(xc,yc,xbeg,ybeg,xend,yend)228956c6c94Sralph arc(xc,yc,xbeg,ybeg,xend,yend)
229956c6c94Sralph int xc,yc,xbeg,ybeg,xend,yend;
230956c6c94Sralph {
231956c6c94Sralph 	double r, radius, costheta, sintheta;
232956c6c94Sralph 	double a, b, c, x, y, tempX;
233956c6c94Sralph 	int right_side;
234956c6c94Sralph 
235956c6c94Sralph 	xbeg -= xc; ybeg -= yc;
236956c6c94Sralph 	xend -= xc; yend -= yc;
237956c6c94Sralph 
238956c6c94Sralph 	/* probably should check that arc is truely circular */
239956c6c94Sralph 	/* Note: r is in screen coordinates. */
240956c6c94Sralph 	r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg);
241956c6c94Sralph 
242956c6c94Sralph 	/*
243956c6c94Sralph 	This method is reasonably efficient, clean, and clever.
244956c6c94Sralph 	The easy part is generating the next point on the arc.  This is
245956c6c94Sralph 	done by rotating the points by the angle theta.  Theta is chosen
246956c6c94Sralph 	so that no rotation will cause more than one pixel of a move.
247956c6c94Sralph 	This corresponds to a triangle having 'x side' of r and 'y side' of 1.
248956c6c94Sralph 	The rotation is done (way) below inside the loop.
249956c6c94Sralph 	*/
250956c6c94Sralph 	if (r <= 1.0) {
251956c6c94Sralph 		/* radius is mapped to length < 1*/
252956c6c94Sralph 		point(xc,yc);
253956c6c94Sralph 		return;
254956c6c94Sralph 		}
255956c6c94Sralph 
256956c6c94Sralph 	radius = sqrt(r*r + 1.0);
257956c6c94Sralph 	sintheta = 1.0/radius;
258956c6c94Sralph 	costheta = r/radius;
259956c6c94Sralph 
260956c6c94Sralph 	/*
261956c6c94Sralph 	The hard part of drawing an arc is figuring out when to stop.
262956c6c94Sralph 	This method works by drawing the line from the beginning point
263956c6c94Sralph 	to the ending point.  This splits the plane in half, with the
264956c6c94Sralph 	arc that we wish to draw on one side of the line.  If we evaluate
265956c6c94Sralph 	side(x,y) = a*x + b*y + c, then all of the points on one side of the
266956c6c94Sralph 	line will result in side being positive, and all the points on the
267956c6c94Sralph 	other side of the line will result in side being negative.
268956c6c94Sralph 
269956c6c94Sralph 	We want to draw the arc in a counter-clockwise direction, so we
270956c6c94Sralph 	must find out what the sign of "side" is for a point which is to the
271956c6c94Sralph 	"right" of a line drawn from "beg" to "end".  A point which must lie
272956c6c94Sralph 	on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)].  (This
273956c6c94Sralph 	point is perpendicular to the line at "beg").
274956c6c94Sralph 
275956c6c94Sralph 	Thus, we compute "side" of the above point, and then compare the
276956c6c94Sralph 	sign of side for each new point with the sign of the above point.
277956c6c94Sralph 	When they are different, we terminate the loop.
278956c6c94Sralph 	*/
279956c6c94Sralph 
280956c6c94Sralph 	a = (double) (yend - ybeg);
281956c6c94Sralph 	b = (double) (xend - xbeg);
282956c6c94Sralph 	c = (double) (yend*xbeg - xend*ybeg);
283956c6c94Sralph 	right_side = side(xbeg + (yend-ybeg),
284956c6c94Sralph 			  ybeg - (xend-xbeg) );
285956c6c94Sralph 
286956c6c94Sralph 	x = xbeg;
287956c6c94Sralph 	y = ybeg;
288956c6c94Sralph 	plot_move(xbeg+xc, ybeg+yc);
289956c6c94Sralph 	do {
290956c6c94Sralph 		dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y ));
291956c6c94Sralph 		/*
292956c6c94Sralph 		screen_move( scaleY(yc + y), scaleX(xc + x) );
293956c6c94Sralph 		plot_addch('*');
294956c6c94Sralph 		*/
295956c6c94Sralph 		tempX = x;
296956c6c94Sralph 		x = x*costheta - y*sintheta;
297956c6c94Sralph 		y = tempX*sintheta + y*costheta;
298956c6c94Sralph 	} while( side(x,y) == right_side );
299956c6c94Sralph }
300