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