|
|
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.