xref: /original-bsd/usr.bin/plot/crtplot.c (revision 556ca7e8)
1 #ifndef lint
2 static char sccsid[] = "@(#)crtplot.c	4.2 (Berkeley) 03/01/91";
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 void closepl();
73 
74 gettmode();
75 if (sp=getenv("TERM"))
76 	setterm(sp);
77 signal(SIGINT, closepl);
78 
79 }
80 
81 
82 
83 
84 void
85 closepl()
86 {
87 signal(SIGINT, SIG_IGN);
88 /* Leave cursor at top of screen. */
89 mvcur(LINES-1, COLS-1, 0, 0);
90 endwin();
91 exit(0);
92 }
93 
94 
95 
96 plot_move(x,y)
97 int x, y;
98 {
99 screen_move(scaleY(y), scaleX(x));
100 }
101 
102 
103 
104 line(x0, y0, x1, y1)
105 int x0, y0, x1, y1;
106 {
107 plot_movech(y0, x0, '*');
108 dda_line('*', scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
109 }
110 
111 label(str)
112 char *str;
113 {
114 	reg i, length;
115 	int strlen();
116 
117 	if ( (length=strlen(str)) > (COLS-lastX) )
118 		length = COLS - lastX;
119 	for (i=0; i<length; ++i)
120 		plot_addch(str[i]);
121 }
122 
123 plot_erase()
124 {
125 /*
126 Some of these functions probably belong in openpl().  However, if the
127 input is being typed in, putting them in openpl would not work
128 since "noecho", etc would prevent (sort of) input.  Notice that
129 the driver calls openpl before doing anything.  This is actually
130 wrong, but it is what whoever originally wrote the driver decided
131 to do.  (openpl() in libplot does nothing -- that is the main problem!)
132 */
133 _puts(TI);
134 _puts(VS);
135 
136 noecho();
137 nonl();
138 tputs(CL, LINES, _putchar);
139 mvcur(0, COLS-1, LINES-1, 0);
140 lastX = 0;
141 lastY = LINES-1;
142 }
143 
144 
145 point(x, y)
146 int x,y;
147 {
148 plot_movech(y, x, '*');
149 }
150 
151 
152 cont(x, y)
153 int x,y;
154 {
155 dda_line('*', lastX-1, lastY, scaleX(x), scaleY(y));
156 }
157 
158 
159 space(x0, y0, x1, y1)
160 int x0, y0, x1, y1;
161 {
162 lowX = (double) x0;
163 lowY = (double) y0;
164 rangeX = COLS/(double) (x1 - x0);
165 rangeY = LINES/(double) (y1 - y0);
166 }
167 
168 
169 linemod(string)
170 char *string;
171 {
172 }
173 
174 
175 
176 /* See Neuman & Sproul for explanation and rationale. */
177 /* Does not plot first point -- assumed that it is already plotted */
178 dda_line(ch, x0, y0, x1, y1)
179 char ch;
180 int x0, y0, x1, y1;	/* already transformed to screen coords */
181 {
182 	int length, i;
183 	double deltaX, deltaY;
184 	double x, y;
185 	double floor();
186 	int abs();
187 
188 length = abs(x1 - x0);
189 if (abs(y1 -y0) > length)
190 	length = abs(y1 - y0);
191 
192 if (length == 0)
193 	return;
194 
195 deltaX = (double) (x1 - x0)/(double) length;
196 deltaY = (double) (y1 - y0)/(double) length;
197 
198 x = (double) x0 + 0.5;
199 y = (double) y0 + 0.5;
200 
201 for (i=0; i < length; ++i)
202 	{
203 	x += deltaX;
204 	y += deltaY;
205 	screen_move((int) floor(y), (int) floor(x));
206 	plot_addch(ch);
207 	}
208 }
209 
210 
211 circle (xc,yc,r)
212 int xc,yc,r;
213 {
214 	arc(xc,yc, xc+r,yc, xc-r,yc);
215 	arc(xc,yc, xc-r,yc, xc+r,yc);
216 }
217 
218 
219 /* should include test for equality? */
220 #define side(x,y)	(a*(x)+b*(y)+c > 0.0 ? 1 : -1)
221 
222 arc(xc,yc,xbeg,ybeg,xend,yend)
223 int xc,yc,xbeg,ybeg,xend,yend;
224 {
225 	double r, radius, costheta, sintheta;
226 	double a, b, c, x, y, tempX;
227 	int right_side;
228 
229 	xbeg -= xc; ybeg -= yc;
230 	xend -= xc; yend -= yc;
231 
232 	/* probably should check that arc is truely circular */
233 	/* Note: r is in screen coordinates. */
234 	r = sqrt( rangeX*rangeX*xbeg*xbeg + rangeY*rangeY*ybeg*ybeg);
235 
236 	/*
237 	This method is reasonably efficient, clean, and clever.
238 	The easy part is generating the next point on the arc.  This is
239 	done by rotating the points by the angle theta.  Theta is chosen
240 	so that no rotation will cause more than one pixel of a move.
241 	This corresponds to a triangle having 'x side' of r and 'y side' of 1.
242 	The rotation is done (way) below inside the loop.
243 	*/
244 	if (r <= 1.0) {
245 		/* radius is mapped to length < 1*/
246 		point(xc,yc);
247 		return;
248 		}
249 
250 	radius = sqrt(r*r + 1.0);
251 	sintheta = 1.0/radius;
252 	costheta = r/radius;
253 
254 	/*
255 	The hard part of drawing an arc is figuring out when to stop.
256 	This method works by drawing the line from the beginning point
257 	to the ending point.  This splits the plane in half, with the
258 	arc that we wish to draw on one side of the line.  If we evaluate
259 	side(x,y) = a*x + b*y + c, then all of the points on one side of the
260 	line will result in side being positive, and all the points on the
261 	other side of the line will result in side being negative.
262 
263 	We want to draw the arc in a counter-clockwise direction, so we
264 	must find out what the sign of "side" is for a point which is to the
265 	"right" of a line drawn from "beg" to "end".  A point which must lie
266 	on the right is [xbeg + (yend-ybeg), ybeg - (xend-xbeg)].  (This
267 	point is perpendicular to the line at "beg").
268 
269 	Thus, we compute "side" of the above point, and then compare the
270 	sign of side for each new point with the sign of the above point.
271 	When they are different, we terminate the loop.
272 	*/
273 
274 	a = (double) (yend - ybeg);
275 	b = (double) (xend - xbeg);
276 	c = (double) (yend*xbeg - xend*ybeg);
277 	right_side = side(xbeg + (yend-ybeg),
278 			  ybeg - (xend-xbeg) );
279 
280 	x = xbeg;
281 	y = ybeg;
282 	plot_move(xbeg+xc, ybeg+yc);
283 	do {
284 		dda_line('*',lastX-1, lastY, scaleX(xc + x), scaleY(yc + y ));
285 		/*
286 		screen_move( scaleY(yc + y), scaleX(xc + x) );
287 		plot_addch('*');
288 		*/
289 		tempX = x;
290 		x = x*costheta - y*sintheta;
291 		y = tempX*sintheta + y*costheta;
292 	} while( side(x,y) == right_side );
293 }
294