From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / triangle.c
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 DEFAULTS       "*delay: 10000 \n"      \
50                                         "*ncolors: 128 \n" \
51                                         "*fpsSolid: true \n" \
52
53 # define SMOOTH_COLORS
54 # include "xlockmore.h"         /* in xscreensaver distribution */
55 #else /* STANDALONE */
56 # include "xlock.h"                     /* in xlockmore distribution */
57 #endif /* STANDALONE */
58
59 ENTRYPOINT ModeSpecOpt triangle_opts =
60 {0, NULL, 0, NULL, NULL};
61
62 #define MAX_STEPS 8
63 #define MAX_SIZE  (1<<MAX_STEPS)
64 #define MAX_LEVELS 1000
65
66 #undef TOP  /* FTSO AIX */
67
68 #define DELTA  0.4
69 #define LEFT   (-0.25)
70 #define RIGHT  1.25
71 #define TOP    0.3
72 #define BOTTOM 1.0
73 #define BLUE   45               /* Just the right shade of blue */
74
75 #define BACKFACE_REMOVAL
76
77 #define DISPLACE(h,d) ((h)/2+LRAND()/(MAXRAND/(2*(d)+1))-d)
78
79 typedef struct {
80         int         width;
81         int         height;
82         int         size;
83         int         steps;
84         int         stage;
85         int         init_now;
86         int         fast;
87         int         i;
88         int         j;
89         int         d;
90         short       level[MAX_LEVELS];
91         int         xpos[2 * MAX_SIZE + 1];
92         int         ypos[MAX_SIZE + 1];
93         short       H[(MAX_SIZE + 1) * (MAX_SIZE + 2) / 2];
94         short      *h[MAX_SIZE + 1];
95         short       delta[MAX_STEPS];
96 } trianglestruct;
97
98 static trianglestruct *triangles = NULL;
99
100 static
101 void
102 draw_atriangle(ModeInfo * mi, XPoint * p, int y_0, int y_1, int y_2, double dinv)
103 {
104         Display    *display = MI_DISPLAY(mi);
105         Window      window = MI_WINDOW(mi);
106         GC          gc = MI_GC(mi);
107
108         if (MI_NCOLORS(mi) > 2) {       /* color */
109                 int         dmax, dmin;
110                 long        color;
111
112                 dmin = MIN(y_0, y_1);
113                 dmin = MIN(dmin, y_2);
114                 dmax = MAX(y_0, y_1);
115                 dmax = MAX(dmax, y_2);
116
117                 if (dmax == 0) {
118                         color = BLUE;
119                 } else {
120                         color = MI_NCOLORS(mi) -
121                                 (int) ((double) MI_NCOLORS(mi) / M_PI_2 * atan(dinv * (dmax - dmin)));
122                 }
123
124                 XSetForeground(display, gc, mi->colors[color % MI_NCOLORS(mi)].pixel);
125                 XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin);
126         } else {
127                 /* mono */
128 #ifdef BACKFACE_REMOVAL
129                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
130                 XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin);
131 #endif
132                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
133                 XDrawLine(display, window, gc, p[0].x, p[0].y, p[1].x, p[1].y);
134                 XDrawLine(display, window, gc, p[1].x, p[1].y, p[2].x, p[2].y);
135                 XDrawLine(display, window, gc, p[2].x, p[2].y, p[0].x, p[0].y);
136         }
137 }
138
139 static
140 void
141 calc_points1(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p)
142 {
143         *y0_p = tp->level[MAX(tp->h[tp->i][tp->j], 0)];
144         *y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)];
145         *y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)];
146
147         p[0].x = tp->xpos[2 * tp->i + tp->j];
148         p[1].x = tp->xpos[2 * (tp->i + d) + tp->j];
149         p[2].x = tp->xpos[2 * tp->i + (tp->j + d)];
150
151         p[0].y = tp->ypos[tp->j] - *y0_p;
152         p[1].y = tp->ypos[tp->j] - *y1_p;
153         p[2].y = tp->ypos[tp->j + d] - *y2_p;
154 }
155
156 static
157 void
158 calc_points2(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p)
159 {
160         *y0_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)];
161         *y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j + d], 0)];
162         *y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)];
163
164         p[0].x = tp->xpos[2 * (tp->i + d) + tp->j];
165         p[1].x = tp->xpos[2 * (tp->i + d) + (tp->j + d)];
166         p[2].x = tp->xpos[2 * tp->i + (tp->j + d)];
167
168         p[0].y = tp->ypos[tp->j] - *y0_p;
169         p[1].y = tp->ypos[tp->j + d] - *y1_p;
170         p[2].y = tp->ypos[tp->j + d] - *y2_p;
171 }
172
173
174 static
175 void
176 draw_mesh(ModeInfo * mi, trianglestruct * tp, int d, int count)
177 {
178         XPoint      p[3];
179         int         first = 1;
180         int         y_0, y_1, y_2;
181         double      dinv = 0.2 / d;
182
183         if ((tp->j == 0) && (tp->i == 0)) {
184 #if 0 /* jwz */
185                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
186 #else
187                 {
188                   int x = 0;
189                   int y = 0;
190                   int x2 = MI_WIN_WIDTH(mi);
191                   int y2 = tp->ypos[0];
192                   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
193                   XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
194                                                  x, y, x2, y2);
195                 }
196 #endif
197         }
198         for (; (tp->j < tp->size) && (count > 0); tp->j += ((count) ? d : 0)) {
199                 for (tp->i = (first) ? tp->i : 0, first = 0;
200                      (tp->i < MAX_SIZE - tp->j) && (count > 0);
201                      tp->i += d, count--) {
202                         if (tp->i + tp->j < tp->size) {
203                                 calc_points1(tp, d, &y_0, &y_1, &y_2, p);
204                                 draw_atriangle(mi, p, y_0, y_1, y_2, dinv);
205                         }
206                         if (tp->i + tp->j + d < tp->size) {
207                                 calc_points2(tp, d, &y_0, &y_1, &y_2, p);
208                                 draw_atriangle(mi, p, y_0, y_1, y_2, dinv);
209                         }
210                 }
211         }
212
213         if (tp->j == tp->size) {
214                 tp->init_now = 1;
215         }
216 }
217
218 ENTRYPOINT void
219 init_triangle (ModeInfo * mi)
220 {
221         trianglestruct *tp;
222         short      *tmp;
223         int         i, dim, one;
224
225         if (triangles == NULL) {
226                 if ((triangles = (trianglestruct *) calloc(MI_NUM_SCREENS(mi),
227                                            sizeof (trianglestruct))) == NULL)
228                         return;
229         }
230         tp = &triangles[MI_SCREEN(mi)];
231
232         tp->width = MI_WIN_WIDTH(mi);
233         tp->height = MI_WIN_HEIGHT(mi);
234         tp->init_now = 1;
235         tp->fast = 2;
236
237         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
238
239
240
241         tp->steps = MAX_STEPS;
242         do {
243                 tp->size = 1 << --tp->steps;
244         } while (tp->size * 5 > tp->width);
245         tmp = tp->H;
246         for (i = 0; i < tp->size + 1; i++) {
247                 tp->h[i] = tmp;
248                 tmp += (tp->size) + 1 - i;
249         }
250
251         tp->stage = -1;
252         dim = MIN(tp->width, tp->height);
253
254         for (i = 0; i < 2 * tp->size + 1; i++) {
255                 tp->xpos[i] = (short) ((((double) i)
256                          / ((double) (2 * tp->size)) * (RIGHT - LEFT) + LEFT)
257                                        * dim) + (tp->width - dim) / 2;
258         }
259
260         for (i = 0; i < (tp->size + 1); i++) {
261                 tp->ypos[i] = (short) ((((double) i)
262                          / ((double) tp->size) * (BOTTOM - TOP) + TOP) * dim)
263                         + (tp->height - dim) / 2;
264         }
265
266         for (i = 0; i < tp->steps; i++) {
267                 tp->delta[i] = ((short) (DELTA * dim)) >> i;
268         }
269
270         one = tp->delta[0];
271
272         if (one > 0)
273                 for (i = 0; i < MAX_LEVELS; i++) {
274                         tp->level[i] = (i * i) / one;
275                 }
276 }
277
278 ENTRYPOINT void
279 draw_triangle (ModeInfo * mi)
280 {
281         trianglestruct *tp = &triangles[MI_SCREEN(mi)];
282         int         d, d2, i, j, delta;
283
284         if (!tp->init_now) {
285                 draw_mesh(mi, tp, tp->d / 2, MAX_SIZE / tp->d);
286
287                 /* The init_now flag will pop up when the scene is complete.
288                  * Cycles specifies how long to wait, in 1/10 secs.
289                  TODO: This is wrong for multi-screens ***
290                  */
291                 if (tp->init_now) {
292 #ifndef STANDALONE
293                         MI_PAUSE(mi) = 2000000;
294 #else
295                         if (tp->stage == -1)
296                           {
297                                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
298                                 if (!mono_p)
299                                   {
300                                         free_colors(mi->xgwa.screen, mi->xgwa.colormap, mi->colors,
301                                                                 mi->npixels);
302                     mi->npixels = 
303                       get_integer_resource (mi->dpy, "ncolors", "Integer");
304                                         make_smooth_colormap (mi->xgwa.screen,
305                                                                                   mi->xgwa.visual, mi->xgwa.colormap,
306                                                                                   mi->colors, &mi->npixels,
307                                                                                   True, &mi->writable_p, True);
308                                   }
309                           }
310 #endif
311                 }
312                 return;
313         }
314         if (tp->delta[0] > 0) {
315                 if (!(++tp->stage)) {
316                         tp->h[0][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
317                         tp->h[tp->size][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
318                         tp->h[0][tp->size] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
319                 } else {
320                         d = 2 << (tp->steps - tp->stage);
321                         d2 = d / 2;
322                         delta = tp->delta[tp->stage - 1];
323
324                         for (i = 0; i < tp->size; i += d) {
325                                 for (j = 0; j < (tp->size - i); j += d) {
326                                         tp->h[i + d2][j] = (short int) DISPLACE(tp->h[i][j] +
327                                                      tp->h[i + d][j], delta);
328                                         tp->h[i][j + d2] = (short int) DISPLACE(tp->h[i][j] +
329                                                      tp->h[i][j + d], delta);
330                                         tp->h[i + d2][j + d2] = (short int) DISPLACE(tp->h[i + d][j] +
331                                                      tp->h[i][j + d], delta);
332                                 }
333
334                                 tp->init_now = 0;
335                                 tp->i = 0;
336                                 tp->j = 0;
337                                 tp->d = d;
338                         }
339                 }
340         }
341         if (tp->stage == tp->steps) {
342                 tp->stage = -1;
343         }
344 }
345
346 ENTRYPOINT void
347 reshape_triangle(ModeInfo * mi, int width, int height)
348 {
349   XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
350   init_triangle (mi);
351 }
352
353 ENTRYPOINT void
354 release_triangle(ModeInfo * mi)
355 {
356         if (triangles != NULL) {
357                 (void) free((void *) triangles);
358                 triangles = NULL;
359         }
360 }
361
362 ENTRYPOINT void
363 refresh_triangle (ModeInfo * mi)
364 {
365         /* Do nothing, it will refresh by itself */
366 }
367
368 ENTRYPOINT Bool
369 triangle_handle_event (ModeInfo *mi, XEvent *event)
370 {
371   if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
372     {
373       reshape_triangle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
374       return True;
375     }
376   return False;
377 }
378
379
380 XSCREENSAVER_MODULE ("Triangle", triangle)