Commit | Line | Data |
---|---|---|
773d3027 RC |
1 | #ifndef lint |
2 | static char sccsid[] = "@(#)crtplot.c 4.1 (Berkeley) %G%"; | |
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 | } |