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