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