xref: /original-bsd/usr.bin/plot/crtplot.c (revision 39c8fdd5)
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 }
293