added "more" command
[unix-history] / usr / src / usr.bin / plot / crtplot.c
CommitLineData
773d3027
RC
1#ifndef lint
2static char sccsid[] = "@(#)crtplot.c 4.1 (Berkeley) %G%";
3#endif
4
5/*
6This plotting routine interprets plot commands and outputs them onto
7intelligent terminals (ie, terminals with clear screen and cursor
8addressability. It uses the curses library. It should be compiled
9as follows:
10 cc crtdriver.c crtplot.c -lcurses -ltermcap -lm
11Note: This requires as slightly modified driver from the standard driver
12because 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
29static double lowX, rangeX; /* min and range of x */
30static double lowY, rangeY; /* min and range of y */
31static int lastX, lastY; /* last point plotted */
32
33
34char *getenv();
35extern char _putchar();
36
37/* This routine just moves the cursor. */
38screen_move(y, x)
39int 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. */
51plot_addch(ch)
52char 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. */
69openpl()
70{
71char *sp;
72int closepl();
73
74gettmode();
75if (sp=getenv("TERM"))
76 setterm(sp);
77signal(SIGINT, closepl);
78
79}
80
81
82
83
84closepl()
85{
86signal(SIGINT, SIG_IGN);
87/* Leave cursor at top of screen. */
88mvcur(LINES-1, COLS-1, 0, 0);
89endwin();
90exit(0);
91}
92
93
94
95plot_move(x,y)
96int x, y;
97{
98screen_move(scaleY(y), scaleX(x));
99}
100
101
102
103line(x0, y0, x1, y1)
104int x0, y0, x1, y1;
105{
106plot_movech(y0, x0, '*');
107dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
108}
109
110label(str)
111char *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
122plot_erase()
123{
124/*
125Some of these functions probably belong in openpl(). However, if the
126input is being typed in, putting them in openpl would not work
127since "noecho", etc would prevent (sort of) input. Notice that
128the driver calls openpl before doing anything. This is actually
129wrong, but it is what whoever originally wrote the driver decided
130to do. (openpl() in libplot does nothing -- that is the main problem!)
131*/
132_puts(TI);
133_puts(VS);
134
135noecho();
136nonl();
137tputs(CL, LINES, _putchar);
138mvcur(0, COLS-1, LINES-1, 0);
139lastX = 0;
140lastY = LINES-1;
141}
142
143
144point(x, y)
145int x,y;
146{
147plot_movech(y, x, '*');
148}
149
150
151cont(x, y)
152int x,y;
153{
154dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
155}
156
157
158space(x0, y0, x1, y1)
159int x0, y0, x1, y1;
160{
161lowX = (double) x0;
162lowY = (double) y0;
163rangeX = COLS/(double) (x1 - x0);
164rangeY = LINES/(double) (y1 - y0);
165}
166
167
168linemod(string)
169char *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 */
177dda_line(ch, x0, y0, x1, y1)
178char ch;
179int 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
187length = abs(x1 - x0);
188if (abs(y1 -y0) > length)
189 length = abs(y1 - y0);
190
191if (length == 0)
192 return;
193
194deltaX = (double) (x1 - x0)/(double) length;
195deltaY = (double) (y1 - y0)/(double) length;
196
197x = (double) x0 + 0.5;
198y = (double) y0 + 0.5;
199
200for (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
210circle (xc,yc,r)
211int 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
221arc(xc,yc,xbeg,ybeg,xend,yend)
222int 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}