1 | /* -*- Mode: C; tab-width: 4 -*- */ |
---|
2 | /* triangle --- create a triangle-mountain */ |
---|
3 | |
---|
4 | #if 0 |
---|
5 | static const char sccsid[] = "@(#)triangle.c 4.04 97/07/28 xlockmore"; |
---|
6 | #endif |
---|
7 | |
---|
8 | /*- |
---|
9 | * Copyright (c) 1995 by Tobias Gloth |
---|
10 | * |
---|
11 | * Permission to use, copy, modify, and distribute this software and its |
---|
12 | * documentation for any purpose and without fee is hereby granted, |
---|
13 | * provided that the above copyright notice appear in all copies and that |
---|
14 | * both that copyright notice and this permission notice appear in |
---|
15 | * supporting documentation. |
---|
16 | * |
---|
17 | * This file is provided AS IS with no warranties of any kind. The author |
---|
18 | * shall have no liability with respect to the infringement of copyrights, |
---|
19 | * trade secrets or any patents by this file or any part thereof. In no |
---|
20 | * event will the author be liable for any lost revenue or profits or |
---|
21 | * other special, indirect and consequential damages. |
---|
22 | * |
---|
23 | * Revision History: |
---|
24 | * 10-May-97: Compatible with xscreensaver |
---|
25 | * 10-Mar-96: re-arranged and re-formatted the code for appearance and |
---|
26 | * to make common subroutines. Simplified. |
---|
27 | * Ron Hitchens <ron@idiom.com> |
---|
28 | * 07-Mar-96: Removed internal delay code, set MI_PAUSE(mi) for inter-scene |
---|
29 | * delays. No other delays are needed here. |
---|
30 | * Made pause time sensitive to value of cycles (in 10ths of a |
---|
31 | * second). Removed (hopefully) all references to globals. |
---|
32 | * Ron Hitchens <ron@idiom.com> |
---|
33 | * 27-Feb-96: Undid the changes listed below. Added ModeInfo argument. |
---|
34 | * Implemented delay between scenes using the MI_PAUSE(mi) |
---|
35 | * scheme. Ron Hitchens <ron@idiom.com> |
---|
36 | * 27-Dec-95: Ron Hitchens <ron@idiom.com> |
---|
37 | * Modified logic of draw_triangle() to provide a delay |
---|
38 | * (sensitive to the value of cycles) between each iteration. |
---|
39 | * Because this mode is so compute intensive, when the new |
---|
40 | * event loop adjusted the delay to compensate, this mode had |
---|
41 | * almost no delay time left. This change pauses between each |
---|
42 | * new landscape, but could still be done better (it is not |
---|
43 | * sensitive to input events while drawing, for example). |
---|
44 | * 03-Nov-95: Many changes (hopefully some good ones) by David Bagley |
---|
45 | * 01-Oct-95: Written by Tobias Gloth |
---|
46 | */ |
---|
47 | |
---|
48 | #ifdef STANDALONE |
---|
49 | # define PROGCLASS "Triangle" |
---|
50 | # define HACK_INIT init_triangle |
---|
51 | # define HACK_DRAW draw_triangle |
---|
52 | # define triangle_opts xlockmore_opts |
---|
53 | # define DEFAULTS "*delay: 10000 \n" \ |
---|
54 | "*ncolors: 128 \n" |
---|
55 | # define SMOOTH_COLORS |
---|
56 | # include "xlockmore.h" /* in xscreensaver distribution */ |
---|
57 | #else /* STANDALONE */ |
---|
58 | # include "xlock.h" /* in xlockmore distribution */ |
---|
59 | #endif /* STANDALONE */ |
---|
60 | |
---|
61 | ModeSpecOpt triangle_opts = |
---|
62 | {0, NULL, 0, NULL, NULL}; |
---|
63 | |
---|
64 | #define MAX_STEPS 8 |
---|
65 | #define MAX_SIZE (1<<MAX_STEPS) |
---|
66 | #define MAX_LEVELS 1000 |
---|
67 | |
---|
68 | #undef TOP /* FTSO AIX */ |
---|
69 | |
---|
70 | #define DELTA 0.4 |
---|
71 | #define LEFT (-0.25) |
---|
72 | #define RIGHT 1.25 |
---|
73 | #define TOP 0.3 |
---|
74 | #define BOTTOM 1.0 |
---|
75 | #define BLUE 45 /* Just the right shade of blue */ |
---|
76 | |
---|
77 | #define BACKFACE_REMOVAL |
---|
78 | |
---|
79 | #define DISPLACE(h,d) ((h)/2+LRAND()/(MAXRAND/(2*(d)+1))-d) |
---|
80 | |
---|
81 | typedef struct { |
---|
82 | int width; |
---|
83 | int height; |
---|
84 | int size; |
---|
85 | int steps; |
---|
86 | int stage; |
---|
87 | int init_now; |
---|
88 | int fast; |
---|
89 | int i; |
---|
90 | int j; |
---|
91 | int d; |
---|
92 | short level[MAX_LEVELS]; |
---|
93 | int xpos[2 * MAX_SIZE + 1]; |
---|
94 | int ypos[MAX_SIZE + 1]; |
---|
95 | short H[(MAX_SIZE + 1) * (MAX_SIZE + 2) / 2]; |
---|
96 | short *h[MAX_SIZE + 1]; |
---|
97 | short delta[MAX_STEPS]; |
---|
98 | } trianglestruct; |
---|
99 | |
---|
100 | static trianglestruct *triangles = NULL; |
---|
101 | |
---|
102 | static |
---|
103 | void |
---|
104 | draw_atriangle(ModeInfo * mi, XPoint * p, int y_0, int y_1, int y_2, double dinv) |
---|
105 | { |
---|
106 | Display *display = MI_DISPLAY(mi); |
---|
107 | Window window = MI_WINDOW(mi); |
---|
108 | GC gc = MI_GC(mi); |
---|
109 | |
---|
110 | if (MI_NPIXELS(mi) > 2) { /* color */ |
---|
111 | int dmax, dmin; |
---|
112 | long color; |
---|
113 | |
---|
114 | dmin = MIN(y_0, y_1); |
---|
115 | dmin = MIN(dmin, y_2); |
---|
116 | dmax = MAX(y_0, y_1); |
---|
117 | dmax = MAX(dmax, y_2); |
---|
118 | |
---|
119 | if (dmax == 0) { |
---|
120 | color = BLUE; |
---|
121 | } else { |
---|
122 | color = MI_NPIXELS(mi) - |
---|
123 | (int) ((double) MI_NPIXELS(mi) / M_PI_2 * atan(dinv * (dmax - dmin))); |
---|
124 | } |
---|
125 | |
---|
126 | XSetForeground(display, gc, MI_PIXEL(mi, color % MI_NPIXELS(mi))); |
---|
127 | XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin); |
---|
128 | } else { |
---|
129 | /* mono */ |
---|
130 | #ifdef BACKFACE_REMOVAL |
---|
131 | XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi)); |
---|
132 | XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin); |
---|
133 | #endif |
---|
134 | XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi)); |
---|
135 | XDrawLine(display, window, gc, p[0].x, p[0].y, p[1].x, p[1].y); |
---|
136 | XDrawLine(display, window, gc, p[1].x, p[1].y, p[2].x, p[2].y); |
---|
137 | XDrawLine(display, window, gc, p[2].x, p[2].y, p[0].x, p[0].y); |
---|
138 | } |
---|
139 | } |
---|
140 | |
---|
141 | static |
---|
142 | void |
---|
143 | calc_points1(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p) |
---|
144 | { |
---|
145 | *y0_p = tp->level[MAX(tp->h[tp->i][tp->j], 0)]; |
---|
146 | *y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)]; |
---|
147 | *y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)]; |
---|
148 | |
---|
149 | p[0].x = tp->xpos[2 * tp->i + tp->j]; |
---|
150 | p[1].x = tp->xpos[2 * (tp->i + d) + tp->j]; |
---|
151 | p[2].x = tp->xpos[2 * tp->i + (tp->j + d)]; |
---|
152 | |
---|
153 | p[0].y = tp->ypos[tp->j] - *y0_p; |
---|
154 | p[1].y = tp->ypos[tp->j] - *y1_p; |
---|
155 | p[2].y = tp->ypos[tp->j + d] - *y2_p; |
---|
156 | } |
---|
157 | |
---|
158 | static |
---|
159 | void |
---|
160 | calc_points2(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p) |
---|
161 | { |
---|
162 | *y0_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)]; |
---|
163 | *y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j + d], 0)]; |
---|
164 | *y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)]; |
---|
165 | |
---|
166 | p[0].x = tp->xpos[2 * (tp->i + d) + tp->j]; |
---|
167 | p[1].x = tp->xpos[2 * (tp->i + d) + (tp->j + d)]; |
---|
168 | p[2].x = tp->xpos[2 * tp->i + (tp->j + d)]; |
---|
169 | |
---|
170 | p[0].y = tp->ypos[tp->j] - *y0_p; |
---|
171 | p[1].y = tp->ypos[tp->j + d] - *y1_p; |
---|
172 | p[2].y = tp->ypos[tp->j + d] - *y2_p; |
---|
173 | } |
---|
174 | |
---|
175 | |
---|
176 | static |
---|
177 | void |
---|
178 | draw_mesh(ModeInfo * mi, trianglestruct * tp, int d, int count) |
---|
179 | { |
---|
180 | XPoint p[3]; |
---|
181 | int first = 1; |
---|
182 | int y_0, y_1, y_2; |
---|
183 | double dinv = 0.2 / d; |
---|
184 | |
---|
185 | if ((tp->j == 0) && (tp->i == 0)) { |
---|
186 | #if 0 /* jwz */ |
---|
187 | XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); |
---|
188 | #else |
---|
189 | { |
---|
190 | int x = 0; |
---|
191 | int y = 0; |
---|
192 | int x2 = MI_WIN_WIDTH(mi); |
---|
193 | int y2 = tp->ypos[0]; |
---|
194 | XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi)); |
---|
195 | XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), |
---|
196 | x, y, x2, y2); |
---|
197 | } |
---|
198 | #endif |
---|
199 | } |
---|
200 | for (; (tp->j < tp->size) && (count > 0); tp->j += ((count) ? d : 0)) { |
---|
201 | for (tp->i = (first) ? tp->i : 0, first = 0; |
---|
202 | (tp->i < MAX_SIZE - tp->j) && (count > 0); |
---|
203 | tp->i += d, count--) { |
---|
204 | if (tp->i + tp->j < tp->size) { |
---|
205 | calc_points1(tp, d, &y_0, &y_1, &y_2, p); |
---|
206 | draw_atriangle(mi, p, y_0, y_1, y_2, dinv); |
---|
207 | } |
---|
208 | if (tp->i + tp->j + d < tp->size) { |
---|
209 | calc_points2(tp, d, &y_0, &y_1, &y_2, p); |
---|
210 | draw_atriangle(mi, p, y_0, y_1, y_2, dinv); |
---|
211 | } |
---|
212 | } |
---|
213 | } |
---|
214 | |
---|
215 | if (tp->j == tp->size) { |
---|
216 | tp->init_now = 1; |
---|
217 | } |
---|
218 | } |
---|
219 | |
---|
220 | void |
---|
221 | init_triangle(ModeInfo * mi) |
---|
222 | { |
---|
223 | trianglestruct *tp; |
---|
224 | short *tmp; |
---|
225 | int i, dim, one; |
---|
226 | |
---|
227 | if (triangles == NULL) { |
---|
228 | if ((triangles = (trianglestruct *) calloc(MI_NUM_SCREENS(mi), |
---|
229 | sizeof (trianglestruct))) == NULL) |
---|
230 | return; |
---|
231 | } |
---|
232 | tp = &triangles[MI_SCREEN(mi)]; |
---|
233 | |
---|
234 | tp->width = MI_WIN_WIDTH(mi); |
---|
235 | tp->height = MI_WIN_HEIGHT(mi); |
---|
236 | tp->init_now = 1; |
---|
237 | tp->fast = 2; |
---|
238 | |
---|
239 | XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); |
---|
240 | |
---|
241 | |
---|
242 | |
---|
243 | tp->steps = MAX_STEPS; |
---|
244 | do { |
---|
245 | tp->size = 1 << --tp->steps; |
---|
246 | } while (tp->size * 5 > tp->width); |
---|
247 | tmp = tp->H; |
---|
248 | for (i = 0; i < tp->size + 1; i++) { |
---|
249 | tp->h[i] = tmp; |
---|
250 | tmp += (tp->size) + 1 - i; |
---|
251 | } |
---|
252 | |
---|
253 | tp->stage = -1; |
---|
254 | dim = MIN(tp->width, tp->height); |
---|
255 | |
---|
256 | for (i = 0; i < 2 * tp->size + 1; i++) { |
---|
257 | tp->xpos[i] = (short) ((((double) i) |
---|
258 | / ((double) (2 * tp->size)) * (RIGHT - LEFT) + LEFT) |
---|
259 | * dim) + (tp->width - dim) / 2; |
---|
260 | } |
---|
261 | |
---|
262 | for (i = 0; i < (tp->size + 1); i++) { |
---|
263 | tp->ypos[i] = (short) ((((double) i) |
---|
264 | / ((double) tp->size) * (BOTTOM - TOP) + TOP) * dim) |
---|
265 | + (tp->height - dim) / 2; |
---|
266 | } |
---|
267 | |
---|
268 | for (i = 0; i < tp->steps; i++) { |
---|
269 | tp->delta[i] = ((short) (DELTA * dim)) >> i; |
---|
270 | } |
---|
271 | |
---|
272 | one = tp->delta[0]; |
---|
273 | |
---|
274 | if (one > 0) |
---|
275 | for (i = 0; i < MAX_LEVELS; i++) { |
---|
276 | tp->level[i] = (i * i) / one; |
---|
277 | } |
---|
278 | } |
---|
279 | |
---|
280 | void |
---|
281 | draw_triangle(ModeInfo * mi) |
---|
282 | { |
---|
283 | trianglestruct *tp = &triangles[MI_SCREEN(mi)]; |
---|
284 | int d, d2, i, j, delta; |
---|
285 | |
---|
286 | if (!tp->init_now) { |
---|
287 | draw_mesh(mi, tp, tp->d / 2, MAX_SIZE / tp->d); |
---|
288 | |
---|
289 | /* The init_now flag will pop up when the scene is complete. |
---|
290 | * Cycles specifies how long to wait, in 1/10 secs. |
---|
291 | TODO: This is wrong for multi-screens *** |
---|
292 | */ |
---|
293 | if (tp->init_now) { |
---|
294 | #ifndef STANDALONE |
---|
295 | MI_PAUSE(mi) = 2000000; |
---|
296 | #else |
---|
297 | if (tp->stage == -1) |
---|
298 | { |
---|
299 | XSync(MI_DISPLAY(mi), False); |
---|
300 | usleep(2000000); |
---|
301 | XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); |
---|
302 | # if 1 |
---|
303 | if (!mono_p) |
---|
304 | { |
---|
305 | free_colors(mi->dpy, mi->xgwa.colormap, mi->colors, |
---|
306 | mi->npixels); |
---|
307 | make_smooth_colormap (mi->dpy, |
---|
308 | mi->xgwa.visual, mi->xgwa.colormap, |
---|
309 | mi->colors, &mi->npixels, |
---|
310 | True, &mi->writable_p, True); |
---|
311 | } |
---|
312 | # endif /* 0 */ |
---|
313 | } |
---|
314 | #endif |
---|
315 | } |
---|
316 | return; |
---|
317 | } |
---|
318 | if (tp->delta[0] > 0) { |
---|
319 | if (!(++tp->stage)) { |
---|
320 | tp->h[0][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0])); |
---|
321 | tp->h[tp->size][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0])); |
---|
322 | tp->h[0][tp->size] = (short int) MAX(0, DISPLACE(0, tp->delta[0])); |
---|
323 | } else { |
---|
324 | d = 2 << (tp->steps - tp->stage); |
---|
325 | d2 = d / 2; |
---|
326 | delta = tp->delta[tp->stage - 1]; |
---|
327 | |
---|
328 | for (i = 0; i < tp->size; i += d) { |
---|
329 | for (j = 0; j < (tp->size - i); j += d) { |
---|
330 | tp->h[i + d2][j] = (short int) DISPLACE(tp->h[i][j] + |
---|
331 | tp->h[i + d][j], delta); |
---|
332 | tp->h[i][j + d2] = (short int) DISPLACE(tp->h[i][j] + |
---|
333 | tp->h[i][j + d], delta); |
---|
334 | tp->h[i + d2][j + d2] = (short int) DISPLACE(tp->h[i + d][j] + |
---|
335 | tp->h[i][j + d], delta); |
---|
336 | } |
---|
337 | |
---|
338 | tp->init_now = 0; |
---|
339 | tp->i = 0; |
---|
340 | tp->j = 0; |
---|
341 | tp->d = d; |
---|
342 | } |
---|
343 | } |
---|
344 | } |
---|
345 | if (tp->stage == tp->steps) { |
---|
346 | tp->stage = -1; |
---|
347 | } |
---|
348 | } |
---|
349 | |
---|
350 | void |
---|
351 | release_triangle(ModeInfo * mi) |
---|
352 | { |
---|
353 | if (triangles != NULL) { |
---|
354 | (void) free((void *) triangles); |
---|
355 | triangles = NULL; |
---|
356 | } |
---|
357 | } |
---|
358 | |
---|
359 | void |
---|
360 | refresh_triangle(ModeInfo * mi) |
---|
361 | { |
---|
362 | /* Do nothing, it will refresh by itself */ |
---|
363 | } |
---|