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