1 /*-
2 * %sccs.include.proprietary.c%
3 */
4
5 #ifndef lint
6 static char sccsid[] = "@(#)crtplot.c 4.7 (Berkeley) 01/07/94";
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. */
screen_move(y,x)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. */
plot_addch(ch)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. */
openpl()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
closepl()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
plot_move(x,y)99 plot_move(x,y)
100 int x, y;
101 {
102 screen_move(scaleY(y), scaleX(x));
103 }
104
105
106
line(x0,y0,x1,y1)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
label(str)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
cputchar(ch)126 cputchar(ch)
127 int ch;
128 {
129 (void)putchar(ch);
130 }
131
plot_erase()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
point(x,y)153 point(x, y)
154 int x,y;
155 {
156 plot_movech(y, x, '*');
157 }
158
159
cont(x,y)160 cont(x, y)
161 int x,y;
162 {
163 dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
164 }
165
166
space(x0,y0,x1,y1)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
linemod(string)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 */
dda_line(ch,x0,y0,x1,y1)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
194 length = abs(x1 - x0);
195 if (abs(y1 -y0) > length)
196 length = abs(y1 - y0);
197
198 if (length == 0)
199 return;
200
201 deltaX = (double) (x1 - x0)/(double) length;
202 deltaY = (double) (y1 - y0)/(double) length;
203
204 x = (double) x0 + 0.5;
205 y = (double) y0 + 0.5;
206
207 for (i=0; i < length; ++i)
208 {
209 x += deltaX;
210 y += deltaY;
211 screen_move((int) floor(y), (int) floor(x));
212 plot_addch(ch);
213 }
214 }
215
216
circle(xc,yc,r)217 circle (xc,yc,r)
218 int xc,yc,r;
219 {
220 arc(xc,yc, xc+r,yc, xc-r,yc);
221 arc(xc,yc, xc-r,yc, xc+r,yc);
222 }
223
224
225 /* should include test for equality? */
226 #define side(x,y) (a*(x)+b*(y)+c > 0.0 ? 1 : -1)
227
arc(xc,yc,xbeg,ybeg,xend,yend)228 arc(xc,yc,xbeg,ybeg,xend,yend)
229 int xc,yc,xbeg,ybeg,xend,yend;
230 {
231 double r, radius, costheta, sintheta;
232 double a, b, c, x, y, tempX;
233 int right_side;
234
235 xbeg -= xc; ybeg -= yc;
236 xend -= xc; yend -= yc;
237
238 /* probably should check that arc is truely circular */
239 /* Note: r is in screen coordinates. */
240 r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg);
241
242 /*
243 This method is reasonably efficient, clean, and clever.
244 The easy part is generating the next point on the arc. This is
245 done by rotating the points by the angle theta. Theta is chosen
246 so that no rotation will cause more than one pixel of a move.
247 This corresponds to a triangle having 'x side' of r and 'y side' of 1.
248 The rotation is done (way) below inside the loop.
249 */
250 if (r <= 1.0) {
251 /* radius is mapped to length < 1*/
252 point(xc,yc);
253 return;
254 }
255
256 radius = sqrt(r*r + 1.0);
257 sintheta = 1.0/radius;
258 costheta = r/radius;
259
260 /*
261 The hard part of drawing an arc is figuring out when to stop.
262 This method works by drawing the line from the beginning point
263 to the ending point. This splits the plane in half, with the
264 arc that we wish to draw on one side of the line. If we evaluate
265 side(x,y) = a*x + b*y + c, then all of the points on one side of the
266 line will result in side being positive, and all the points on the
267 other side of the line will result in side being negative.
268
269 We want to draw the arc in a counter-clockwise direction, so we
270 must find out what the sign of "side" is for a point which is to the
271 "right" of a line drawn from "beg" to "end". A point which must lie
272 on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)]. (This
273 point is perpendicular to the line at "beg").
274
275 Thus, we compute "side" of the above point, and then compare the
276 sign of side for each new point with the sign of the above point.
277 When they are different, we terminate the loop.
278 */
279
280 a = (double) (yend - ybeg);
281 b = (double) (xend - xbeg);
282 c = (double) (yend*xbeg - xend*ybeg);
283 right_side = side(xbeg + (yend-ybeg),
284 ybeg - (xend-xbeg) );
285
286 x = xbeg;
287 y = ybeg;
288 plot_move(xbeg+xc, ybeg+yc);
289 do {
290 dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y ));
291 /*
292 screen_move( scaleY(yc + y), scaleX(xc + x) );
293 plot_addch('*');
294 */
295 tempX = x;
296 x = x*costheta - y*sintheta;
297 y = tempX*sintheta + y*costheta;
298 } while( side(x,y) == right_side );
299 }
300