xref: /csrg-svn/usr.bin/plot/crtplot.c (revision 46844)
115505Sralph #ifndef lint
2*46844Sbostic static char sccsid[] = "@(#)crtplot.c	4.2 (Berkeley) 03/01/91";
315505Sralph #endif
415505Sralph 
515505Sralph /*
615505Sralph This plotting routine interprets plot commands and outputs them onto
715505Sralph intelligent terminals (ie, terminals with clear screen and cursor
815505Sralph addressability.  It uses the curses library.  It should be compiled
915505Sralph as follows:
1015505Sralph 	cc crtdriver.c crtplot.c -lcurses -ltermcap -lm
1115505Sralph Note:  This requires as slightly modified driver from the standard driver
1215505Sralph because some function names conflicted with the curses library.
1315505Sralph (That's what you get when you have a flat name space!)
1415505Sralph */
1515505Sralph 
1615505Sralph 
1715505Sralph #include <curses.h>
1815505Sralph #include <math.h>
1915505Sralph #include <signal.h>
2015505Sralph 
2115505Sralph 
2215505Sralph /*  These map from plot routine coordinates to screen coordinates.  */
2315505Sralph #define scaleX(x)		(int) ((x-lowX)*rangeX + 0.5)
2415505Sralph #define scaleY(y)		(int) (LINES-0.5 - ((y-lowY)*rangeY))
2515505Sralph 
2615505Sralph #define plot_movech(y, x, ch)	{ plot_move(x, y); plot_addch(ch); }
2715505Sralph 
2815505Sralph 
2915505Sralph static double lowX, rangeX;	/* min and range of x */
3015505Sralph static double lowY, rangeY;	/* min and range of y */
3115505Sralph static int lastX, lastY;	/* last point plotted */
3215505Sralph 
3315505Sralph 
3415505Sralph char *getenv();
3515505Sralph extern char _putchar();
3615505Sralph 
3715505Sralph /* This routine just moves the cursor. */
3815505Sralph screen_move(y, x)
3915505Sralph int x,y;
4015505Sralph {
4115505Sralph 	/* must check for automatic wrap at last col */
4215505Sralph 	if (!AM || (y < LINES -1) || (x < COLS -1)) {
4315505Sralph 		mvcur(lastY, lastX, y, x);
4415505Sralph 		lastY = y;
4515505Sralph 		lastX = x;
4615505Sralph 		}
4715505Sralph }
4815505Sralph 
4915505Sralph 
5015505Sralph /* This routine assumes the cursor is positioned correctly. */
5115505Sralph plot_addch(ch)
5215505Sralph char ch;
5315505Sralph {
5415505Sralph 	putchar(ch);
5515505Sralph 	if (++lastX >= COLS) {
5615505Sralph 		if (AM) {
5715505Sralph 			lastX = 0;
5815505Sralph 			lastY++;
5915505Sralph 		} else {
6015505Sralph 			lastX = COLS - 1;
6115505Sralph 			}
6215505Sralph 		}
6315505Sralph }
6415505Sralph 
6515505Sralph 
6615505Sralph 
6715505Sralph 
6815505Sralph /* See the curses manual for what is been done and why. */
6915505Sralph openpl()
7015505Sralph {
7115505Sralph char *sp;
72*46844Sbostic void closepl();
7315505Sralph 
7415505Sralph gettmode();
7515505Sralph if (sp=getenv("TERM"))
7615505Sralph 	setterm(sp);
7715505Sralph signal(SIGINT, closepl);
7815505Sralph 
7915505Sralph }
8015505Sralph 
8115505Sralph 
8215505Sralph 
8315505Sralph 
84*46844Sbostic void
8515505Sralph closepl()
8615505Sralph {
8715505Sralph signal(SIGINT, SIG_IGN);
8815505Sralph /* Leave cursor at top of screen. */
8915505Sralph mvcur(LINES-1, COLS-1, 0, 0);
9015505Sralph endwin();
9115505Sralph exit(0);
9215505Sralph }
9315505Sralph 
9415505Sralph 
9515505Sralph 
9615505Sralph plot_move(x,y)
9715505Sralph int x, y;
9815505Sralph {
9915505Sralph screen_move(scaleY(y), scaleX(x));
10015505Sralph }
10115505Sralph 
10215505Sralph 
10315505Sralph 
10415505Sralph line(x0, y0, x1, y1)
10515505Sralph int x0, y0, x1, y1;
10615505Sralph {
10715505Sralph plot_movech(y0, x0, '*');
10815505Sralph dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
10915505Sralph }
11015505Sralph 
11115505Sralph label(str)
11215505Sralph char *str;
11315505Sralph {
11415505Sralph 	reg i, length;
11515505Sralph 	int strlen();
11615505Sralph 
11715505Sralph 	if ( (length=strlen(str)) > (COLS-lastX) )
11815505Sralph 		length = COLS - lastX;
11915505Sralph 	for (i=0; i<length; ++i)
12015505Sralph 		plot_addch(str[i]);
12115505Sralph }
12215505Sralph 
12315505Sralph plot_erase()
12415505Sralph {
12515505Sralph /*
12615505Sralph Some of these functions probably belong in openpl().  However, if the
12715505Sralph input is being typed in, putting them in openpl would not work
12815505Sralph since "noecho", etc would prevent (sort of) input.  Notice that
12915505Sralph the driver calls openpl before doing anything.  This is actually
13015505Sralph wrong, but it is what whoever originally wrote the driver decided
13115505Sralph to do.  (openpl() in libplot does nothing -- that is the main problem!)
13215505Sralph */
13315505Sralph _puts(TI);
13415505Sralph _puts(VS);
13515505Sralph 
13615505Sralph noecho();
13715505Sralph nonl();
13815505Sralph tputs(CL, LINES, _putchar);
13915505Sralph mvcur(0, COLS-1, LINES-1, 0);
14015505Sralph lastX = 0;
14115505Sralph lastY = LINES-1;
14215505Sralph }
14315505Sralph 
14415505Sralph 
14515505Sralph point(x, y)
14615505Sralph int x,y;
14715505Sralph {
14815505Sralph plot_movech(y, x, '*');
14915505Sralph }
15015505Sralph 
15115505Sralph 
15215505Sralph cont(x, y)
15315505Sralph int x,y;
15415505Sralph {
15515505Sralph dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
15615505Sralph }
15715505Sralph 
15815505Sralph 
15915505Sralph space(x0, y0, x1, y1)
16015505Sralph int x0, y0, x1, y1;
16115505Sralph {
16215505Sralph lowX = (double) x0;
16315505Sralph lowY = (double) y0;
16415505Sralph rangeX = COLS/(double) (x1 - x0);
16515505Sralph rangeY = LINES/(double) (y1 - y0);
16615505Sralph }
16715505Sralph 
16815505Sralph 
16915505Sralph linemod(string)
17015505Sralph char *string;
17115505Sralph {
17215505Sralph }
17315505Sralph 
17415505Sralph 
17515505Sralph 
17615505Sralph /* See Neuman & Sproul for explanation and rationale. */
17715505Sralph /* Does not plot first point -- assumed that it is already plotted */
17815505Sralph dda_line(ch, x0, y0, x1, y1)
17915505Sralph char ch;
18015505Sralph int x0, y0, x1, y1;	/* already transformed to screen coords */
18115505Sralph {
18215505Sralph 	int length, i;
18315505Sralph 	double deltaX, deltaY;
18415505Sralph 	double x, y;
18515505Sralph 	double floor();
18615505Sralph 	int abs();
18715505Sralph 
18815505Sralph length = abs(x1 - x0);
18915505Sralph if (abs(y1 -y0) > length)
19015505Sralph 	length = abs(y1 - y0);
19115505Sralph 
19215505Sralph if (length == 0)
19315505Sralph 	return;
19415505Sralph 
19515505Sralph deltaX = (double) (x1 - x0)/(double) length;
19615505Sralph deltaY = (double) (y1 - y0)/(double) length;
19715505Sralph 
19815505Sralph x = (double) x0 + 0.5;
19915505Sralph y = (double) y0 + 0.5;
20015505Sralph 
20115505Sralph for (i=0; i < length; ++i)
20215505Sralph 	{
20315505Sralph 	x += deltaX;
20415505Sralph 	y += deltaY;
20515505Sralph 	screen_move((int) floor(y), (int) floor(x));
20615505Sralph 	plot_addch(ch);
20715505Sralph 	}
20815505Sralph }
20915505Sralph 
21015505Sralph 
21115505Sralph circle (xc,yc,r)
21215505Sralph int xc,yc,r;
21315505Sralph {
21415505Sralph 	arc(xc,yc, xc+r,yc, xc-r,yc);
21515505Sralph 	arc(xc,yc, xc-r,yc, xc+r,yc);
21615505Sralph }
21715505Sralph 
21815505Sralph 
21915505Sralph /* should include test for equality? */
22015505Sralph #define side(x,y)	(a*(x)+b*(y)+c > 0.0 ? 1 : -1)
22115505Sralph 
22215505Sralph arc(xc,yc,xbeg,ybeg,xend,yend)
22315505Sralph int xc,yc,xbeg,ybeg,xend,yend;
22415505Sralph {
22515505Sralph 	double r, radius, costheta, sintheta;
22615505Sralph 	double a, b, c, x, y, tempX;
22715505Sralph 	int right_side;
22815505Sralph 
22915505Sralph 	xbeg -= xc; ybeg -= yc;
23015505Sralph 	xend -= xc; yend -= yc;
23115505Sralph 
23215505Sralph 	/* probably should check that arc is truely circular */
23315505Sralph 	/* Note: r is in screen coordinates. */
23415505Sralph 	r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg);
23515505Sralph 
23615505Sralph 	/*
23715505Sralph 	This method is reasonably efficient, clean, and clever.
23815505Sralph 	The easy part is generating the next point on the arc.  This is
23915505Sralph 	done by rotating the points by the angle theta.  Theta is chosen
24015505Sralph 	so that no rotation will cause more than one pixel of a move.
24115505Sralph 	This corresponds to a triangle having 'x side' of r and 'y side' of 1.
24215505Sralph 	The rotation is done (way) below inside the loop.
24315505Sralph 	*/
24415505Sralph 	if (r <= 1.0) {
24515505Sralph 		/* radius is mapped to length < 1*/
24615505Sralph 		point(xc,yc);
24715505Sralph 		return;
24815505Sralph 		}
24915505Sralph 
25015505Sralph 	radius = sqrt(r*r + 1.0);
25115505Sralph 	sintheta = 1.0/radius;
25215505Sralph 	costheta = r/radius;
25315505Sralph 
25415505Sralph 	/*
25515505Sralph 	The hard part of drawing an arc is figuring out when to stop.
25615505Sralph 	This method works by drawing the line from the beginning point
25715505Sralph 	to the ending point.  This splits the plane in half, with the
25815505Sralph 	arc that we wish to draw on one side of the line.  If we evaluate
25915505Sralph 	side(x,y) = a*x + b*y + c, then all of the points on one side of the
26015505Sralph 	line will result in side being positive, and all the points on the
26115505Sralph 	other side of the line will result in side being negative.
26215505Sralph 
26315505Sralph 	We want to draw the arc in a counter-clockwise direction, so we
26415505Sralph 	must find out what the sign of "side" is for a point which is to the
26515505Sralph 	"right" of a line drawn from "beg" to "end".  A point which must lie
26615505Sralph 	on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)].  (This
26715505Sralph 	point is perpendicular to the line at "beg").
26815505Sralph 
26915505Sralph 	Thus, we compute "side" of the above point, and then compare the
27015505Sralph 	sign of side for each new point with the sign of the above point.
27115505Sralph 	When they are different, we terminate the loop.
27215505Sralph 	*/
27315505Sralph 
27415505Sralph 	a = (double) (yend - ybeg);
27515505Sralph 	b = (double) (xend - xbeg);
27615505Sralph 	c = (double) (yend*xbeg - xend*ybeg);
27715505Sralph 	right_side = side(xbeg + (yend-ybeg),
27815505Sralph 			  ybeg - (xend-xbeg) );
27915505Sralph 
28015505Sralph 	x = xbeg;
28115505Sralph 	y = ybeg;
28215505Sralph 	plot_move(xbeg+xc, ybeg+yc);
28315505Sralph 	do {
28415505Sralph 		dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y ));
28515505Sralph 		/*
28615505Sralph 		screen_move( scaleY(yc + y), scaleX(xc + x) );
28715505Sralph 		plot_addch('*');
28815505Sralph 		*/
28915505Sralph 		tempX = x;
29015505Sralph 		x = x*costheta - y*sintheta;
29115505Sralph 		y = tempX*sintheta + y*costheta;
29215505Sralph 	} while( side(x,y) == right_side );
29315505Sralph }
294