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