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