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