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