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 } 293