1 #ifndef lint 2 static char sccsid[] = "@(#)crtplot.c 4.2 (Berkeley) 03/01/91"; 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 void closepl(); 73 74 gettmode(); 75 if (sp=getenv("TERM")) 76 setterm(sp); 77 signal(SIGINT, closepl); 78 79 } 80 81 82 83 84 void 85 closepl() 86 { 87 signal(SIGINT, SIG_IGN); 88 /* Leave cursor at top of screen. */ 89 mvcur(LINES-1, COLS-1, 0, 0); 90 endwin(); 91 exit(0); 92 } 93 94 95 96 plot_move(x,y) 97 int x, y; 98 { 99 screen_move(scaleY(y), scaleX(x)); 100 } 101 102 103 104 line(x0, y0, x1, y1) 105 int x0, y0, x1, y1; 106 { 107 plot_movech(y0, x0, '*'); 108 dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1)); 109 } 110 111 label(str) 112 char *str; 113 { 114 reg i, length; 115 int strlen(); 116 117 if ( (length=strlen(str)) > (COLS-lastX) ) 118 length = COLS - lastX; 119 for (i=0; i<length; ++i) 120 plot_addch(str[i]); 121 } 122 123 plot_erase() 124 { 125 /* 126 Some of these functions probably belong in openpl(). However, if the 127 input is being typed in, putting them in openpl would not work 128 since "noecho", etc would prevent (sort of) input. Notice that 129 the driver calls openpl before doing anything. This is actually 130 wrong, but it is what whoever originally wrote the driver decided 131 to do. (openpl() in libplot does nothing -- that is the main problem!) 132 */ 133 _puts(TI); 134 _puts(VS); 135 136 noecho(); 137 nonl(); 138 tputs(CL, LINES, _putchar); 139 mvcur(0, COLS-1, LINES-1, 0); 140 lastX = 0; 141 lastY = LINES-1; 142 } 143 144 145 point(x, y) 146 int x,y; 147 { 148 plot_movech(y, x, '*'); 149 } 150 151 152 cont(x, y) 153 int x,y; 154 { 155 dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y)); 156 } 157 158 159 space(x0, y0, x1, y1) 160 int x0, y0, x1, y1; 161 { 162 lowX = (double) x0; 163 lowY = (double) y0; 164 rangeX = COLS/(double) (x1 - x0); 165 rangeY = LINES/(double) (y1 - y0); 166 } 167 168 169 linemod(string) 170 char *string; 171 { 172 } 173 174 175 176 /* See Neuman & Sproul for explanation and rationale. */ 177 /* Does not plot first point -- assumed that it is already plotted */ 178 dda_line(ch, x0, y0, x1, y1) 179 char ch; 180 int x0, y0, x1, y1; /* already transformed to screen coords */ 181 { 182 int length, i; 183 double deltaX, deltaY; 184 double x, y; 185 double floor(); 186 int abs(); 187 188 length = abs(x1 - x0); 189 if (abs(y1 -y0) > length) 190 length = abs(y1 - y0); 191 192 if (length == 0) 193 return; 194 195 deltaX = (double) (x1 - x0)/(double) length; 196 deltaY = (double) (y1 - y0)/(double) length; 197 198 x = (double) x0 + 0.5; 199 y = (double) y0 + 0.5; 200 201 for (i=0; i < length; ++i) 202 { 203 x += deltaX; 204 y += deltaY; 205 screen_move((int) floor(y), (int) floor(x)); 206 plot_addch(ch); 207 } 208 } 209 210 211 circle (xc,yc,r) 212 int xc,yc,r; 213 { 214 arc(xc,yc, xc+r,yc, xc-r,yc); 215 arc(xc,yc, xc-r,yc, xc+r,yc); 216 } 217 218 219 /* should include test for equality? */ 220 #define side(x,y) (a*(x)+b*(y)+c > 0.0 ? 1 : -1) 221 222 arc(xc,yc,xbeg,ybeg,xend,yend) 223 int xc,yc,xbeg,ybeg,xend,yend; 224 { 225 double r, radius, costheta, sintheta; 226 double a, b, c, x, y, tempX; 227 int right_side; 228 229 xbeg -= xc; ybeg -= yc; 230 xend -= xc; yend -= yc; 231 232 /* probably should check that arc is truely circular */ 233 /* Note: r is in screen coordinates. */ 234 r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg); 235 236 /* 237 This method is reasonably efficient, clean, and clever. 238 The easy part is generating the next point on the arc. This is 239 done by rotating the points by the angle theta. Theta is chosen 240 so that no rotation will cause more than one pixel of a move. 241 This corresponds to a triangle having 'x side' of r and 'y side' of 1. 242 The rotation is done (way) below inside the loop. 243 */ 244 if (r <= 1.0) { 245 /* radius is mapped to length < 1*/ 246 point(xc,yc); 247 return; 248 } 249 250 radius = sqrt(r*r + 1.0); 251 sintheta = 1.0/radius; 252 costheta = r/radius; 253 254 /* 255 The hard part of drawing an arc is figuring out when to stop. 256 This method works by drawing the line from the beginning point 257 to the ending point. This splits the plane in half, with the 258 arc that we wish to draw on one side of the line. If we evaluate 259 side(x,y) = a*x + b*y + c, then all of the points on one side of the 260 line will result in side being positive, and all the points on the 261 other side of the line will result in side being negative. 262 263 We want to draw the arc in a counter-clockwise direction, so we 264 must find out what the sign of "side" is for a point which is to the 265 "right" of a line drawn from "beg" to "end". A point which must lie 266 on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)]. (This 267 point is perpendicular to the line at "beg"). 268 269 Thus, we compute "side" of the above point, and then compare the 270 sign of side for each new point with the sign of the above point. 271 When they are different, we terminate the loop. 272 */ 273 274 a = (double) (yend - ybeg); 275 b = (double) (xend - xbeg); 276 c = (double) (yend*xbeg - xend*ybeg); 277 right_side = side(xbeg + (yend-ybeg), 278 ybeg - (xend-xbeg) ); 279 280 x = xbeg; 281 y = ybeg; 282 plot_move(xbeg+xc, ybeg+yc); 283 do { 284 dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y )); 285 /* 286 screen_move( scaleY(yc + y), scaleX(xc + x) ); 287 plot_addch('*'); 288 */ 289 tempX = x; 290 x = x*costheta - y*sintheta; 291 y = tempX*sintheta + y*costheta; 292 } while( side(x,y) == right_side ); 293 } 294