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