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