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