ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / hacks / drift.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * drift --- drifting recursive fractal cosmic flames.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)drift.c       4.02 97/04/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1991 by Patrick J. Naughton.
9  *
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 10-May-97: jwz@netscape.com: turned into a standalone program.
24  * 01-Jan-97: Moved new flame to drift.  Compile time options now run time.
25  * 01-Jun-95: Updated by Scott Draves.
26  * 27-Jun-91: vary number of functions used.
27  * 24-Jun-91: fixed portability problem with integer mod (%).
28  * 06-Jun-91: Written. (received from Scott Draves, spot@cs.cmu.edu).
29  */
30
31 #ifdef STANDALONE
32 # define PROGCLASS                                      "Drift"
33 # define HACK_INIT                                      init_drift
34 # define HACK_DRAW                                      draw_drift
35 # define drift_opts                                     xlockmore_opts
36 # define DEFAULTS       "*count:                30    \n"                       \
37                                         "*delay:                10000 \n"                       \
38                                         "*ncolors:              200   \n"                       \
39                                         "*eraseSpeed:   400 \n"                         \
40                                         "*eraseMode:    -1 \n"
41 # define SMOOTH_COLORS
42 # include "xlockmore.h"                         /* from the xscreensaver distribution */
43 # include "erase.h"
44 #else  /* !STANDALONE */
45 # include "xlock.h"                                     /* from the xlockmore distribution */
46 #endif /* !STANDALONE */
47
48
49 #define MAXBATCH1       200     /* mono */
50 #define MAXBATCH2       20      /* color */
51 #define FUSE            10      /* discard this many initial iterations */
52 #define NMAJORVARS      7
53 #define MAXLEV 10
54
55 #define DEF_GROW "False"        /* Grow fractals instead of animating one at a time,
56                                    would then be like flame */
57 #define DEF_LISS "False"        /* if this is defined then instead of a point
58                                    bouncing around in a high dimensional sphere, we
59                                    use lissojous figures.  Only makes sense if
60                                    grow is false. */
61
62 static Bool grow;
63 static Bool liss;
64
65 static XrmOptionDescRec opts[] =
66 {
67         {"-grow", ".drift.grow", XrmoptionNoArg, (caddr_t) "on"},
68         {"+grow", ".drift.grow", XrmoptionNoArg, (caddr_t) "off"},
69         {"-liss", ".drift.trail", XrmoptionNoArg, (caddr_t) "on"},
70         {"+liss", ".drift.trail", XrmoptionNoArg, (caddr_t) "off"}
71 };
72 static argtype vars[] =
73 {
74         {(caddr_t *) & grow, "grow", "Grow", DEF_GROW, t_Bool},
75         {(caddr_t *) & liss, "liss", "Liss", DEF_LISS, t_Bool}
76 };
77 static OptionStruct desc[] =
78 {
79         {"-/+grow", "turn on/off growing fractals, else they are animated"},
80         {"-/+liss", "turn on/off using lissojous figures to get points"}
81 };
82
83 ModeSpecOpt drift_opts = { 4, opts, 2, vars, desc };
84
85
86 typedef struct {
87         /* shape of current flame */
88         int         nxforms;
89         double      f[2][3][MAXLEV];    /* a bunch of non-homogeneous xforms */
90         int         variation[10];      /* for each xform */
91
92         /* Animation */
93         double      df[2][3][MAXLEV];
94
95         /* high-level control */
96         int         mode;       /* 0->slow/single 1->fast/many */
97         int         nfractals;  /* draw this many fractals */
98         int         major_variation;
99         int         fractal_len;        /* pts/fractal */
100         int         color;
101         int         rainbow;    /* more than one color per fractal
102                                    1-> computed by adding dimension to fractal */
103
104         int         width, height;      /* of window */
105         int         timer;
106
107         /* draw info about current flame */
108         int         fuse;       /* iterate this many before drawing */
109         int         total_points;       /* draw this many pts before fractal ends */
110         int         npoints;    /* how many we've computed but not drawn */
111         XPoint      pts[MAXBATCH1];     /* here they are */
112         unsigned long pixcol;
113         /* when drawing in color, we have a buffer per color */
114         int         ncpoints[NUMCOLORS];
115         XPoint      cpts[NUMCOLORS][MAXBATCH2];
116
117         double      x, y, c;
118         int         liss_time;
119         Bool        grow, liss;
120 } driftstruct;
121
122 static driftstruct *drifts = NULL;
123
124 static short
125 halfrandom(int mv)
126 {
127         static short lasthalf = 0;
128         unsigned long r;
129
130         if (lasthalf) {
131                 r = lasthalf;
132                 lasthalf = 0;
133         } else {
134                 r = LRAND();
135                 lasthalf = r >> 16;
136         }
137         r = r % mv;
138         return r;
139 }
140
141 static int
142 frandom(int n)
143 {
144         static long saved_random_bits = 0;
145         static int  nbits = 0;
146         int         result;
147
148         if (3 > nbits) {
149                 saved_random_bits = LRAND();
150                 nbits = 31;
151         }
152         switch (n) {
153                 case 2:
154                         result = saved_random_bits & 1;
155                         saved_random_bits >>= 1;
156                         nbits -= 1;
157                         return result;
158
159                 case 3:
160                         result = saved_random_bits & 3;
161                         saved_random_bits >>= 2;
162                         nbits -= 2;
163                         if (3 == result)
164                                 return frandom(3);
165                         return result;
166
167                 case 4:
168                         result = saved_random_bits & 3;
169                         saved_random_bits >>= 2;
170                         nbits -= 2;
171                         return result;
172
173                 case 5:
174                         result = saved_random_bits & 7;
175                         saved_random_bits >>= 3;
176                         nbits -= 3;
177                         if (4 < result)
178                                 return frandom(5);
179                         return result;
180                 default:
181                         (void) fprintf(stderr, "bad arg to frandom\n");
182                         exit(1);
183         }
184         return 0;
185 }
186
187 #define DISTRIB_A (halfrandom(7000) + 9000)
188 #define DISTRIB_B ((frandom(3) + 1) * (frandom(3) + 1) * 120000)
189 #define LEN(x) (sizeof(x)/sizeof((x)[0]))
190
191 static void
192 initmode(ModeInfo * mi, int mode)
193 {
194         driftstruct *dp = &drifts[MI_SCREEN(mi)];
195
196 #define VARIATION_LEN 14
197
198         dp->mode = mode;
199
200         dp->major_variation = halfrandom(VARIATION_LEN);
201         /*  0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 6 */
202         dp->major_variation = ((dp->major_variation >= VARIATION_LEN >> 1) &&
203                                (dp->major_variation < VARIATION_LEN - 1)) ?
204                 (dp->major_variation + 1) >> 1 : dp->major_variation >> 1;
205
206         if (dp->grow) {
207                 dp->rainbow = 0;
208                 if (mode) {
209                         if (!dp->color || halfrandom(8)) {
210                                 dp->nfractals = halfrandom(30) + 5;
211                                 dp->fractal_len = DISTRIB_A;
212                         } else {
213                                 dp->nfractals = halfrandom(5) + 5;
214                                 dp->fractal_len = DISTRIB_B;
215                         }
216                 } else {
217                         dp->rainbow = dp->color;
218                         dp->nfractals = 1;
219                         dp->fractal_len = DISTRIB_B;
220                 }
221         } else {
222                 dp->nfractals = 1;
223                 dp->rainbow = dp->color;
224                 dp->fractal_len = 2000000;
225         }
226         dp->fractal_len = (dp->fractal_len * MI_BATCHCOUNT(mi)) / 20;
227         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
228 }
229
230 static void
231 pick_df_coefs(ModeInfo * mi)
232 {
233         driftstruct *dp = &drifts[MI_SCREEN(mi)];
234         int         i, j, k;
235         double      r;
236
237         for (i = 0; i < dp->nxforms; i++) {
238
239                 r = 1e-6;
240                 for (j = 0; j < 2; j++)
241                         for (k = 0; k < 3; k++) {
242                                 dp->df[j][k][i] = ((double) halfrandom(1000) / 500.0 - 1.0);
243                                 r += dp->df[j][k][i] * dp->df[j][k][i];
244                         }
245                 r = (3 + halfrandom(5)) * 0.01 / sqrt(r);
246                 for (j = 0; j < 2; j++)
247                         for (k = 0; k < 3; k++)
248                                 dp->df[j][k][i] *= r;
249         }
250 }
251
252 static void
253 initfractal(ModeInfo * mi)
254 {
255         driftstruct *dp = &drifts[MI_SCREEN(mi)];
256         int         i, j, k;
257
258 #define XFORM_LEN 9
259
260         dp->fuse = FUSE;
261         dp->total_points = 0;
262         if (dp->rainbow)
263                 for (i = 0; i < MI_NPIXELS(mi); i++)
264                         dp->ncpoints[i] = 0;
265         else
266                 dp->npoints = 0;
267         dp->nxforms = halfrandom(XFORM_LEN);
268         /* 2, 2, 2, 3, 3, 3, 4, 4, 5 */
269         dp->nxforms = (dp->nxforms >= XFORM_LEN - 1) + dp->nxforms / 3 + 2;
270
271         dp->c = dp->x = dp->y = 0.0;
272         if (dp->liss && !halfrandom(10)) {
273                 dp->liss_time = 0;
274         }
275         if (!dp->grow)
276                 pick_df_coefs(mi);
277         for (i = 0; i < dp->nxforms; i++) {
278                 if (NMAJORVARS == dp->major_variation)
279                         dp->variation[i] = halfrandom(NMAJORVARS);
280                 else
281                         dp->variation[i] = dp->major_variation;
282                 for (j = 0; j < 2; j++)
283                         for (k = 0; k < 3; k++) {
284                                 if (dp->liss)
285                                         dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
286                                 else
287                                         dp->f[j][k][i] = ((double) halfrandom(1000) / 500.0 - 1.0);
288                         }
289         }
290         if (dp->color)
291                 dp->pixcol = MI_PIXEL(mi, halfrandom(MI_NPIXELS(mi)));
292         else
293                 dp->pixcol = MI_WIN_WHITE_PIXEL(mi);
294
295 }
296
297
298 void
299 init_drift(ModeInfo * mi)
300 {
301         driftstruct *dp;
302
303         if (drifts == NULL) {
304                 if ((drifts = (driftstruct *) calloc(MI_NUM_SCREENS(mi),
305                                               sizeof (driftstruct))) == NULL)
306                         return;
307         }
308         dp = &drifts[MI_SCREEN(mi)];
309
310         dp->width = MI_WIN_WIDTH(mi);
311         dp->height = MI_WIN_HEIGHT(mi);
312         dp->color = MI_NPIXELS(mi) > 2;
313
314         if (MI_WIN_IS_FULLRANDOM(mi)) {
315 #if 1 /* jwz: even up the odds */
316           switch ((int) (LRAND() % 3)) {
317           case 0:   dp->grow = True;  dp->liss = False; break;
318           case 1:   dp->grow = False; dp->liss = True;  break;
319           default:  dp->grow = False; dp->liss = False; break;
320                 /* liss and grow don't work together. */
321           }
322 #else /* 0 */
323                 if (LRAND() & 1)
324                         dp->grow = True;
325                 else {
326                         dp->grow = False;
327                         dp->liss = (Bool) (LRAND() & 1);
328                 }
329 #endif
330         } else {
331                 dp->grow = grow;
332                 if (dp->grow)
333                         dp->liss = False;
334                 else
335                         dp->liss = liss;
336         }
337         initmode(mi, 1);
338         initfractal(mi);
339 }
340
341 static void
342 iter(driftstruct * dp)
343 {
344         int         i = frandom(dp->nxforms);
345         double      nx, ny, nc;
346
347
348         if (i)
349                 nc = (dp->c + 1.0) / 2.0;
350         else
351                 nc = dp->c / 2.0;
352
353         nx = dp->f[0][0][i] * dp->x + dp->f[0][1][i] * dp->y + dp->f[0][2][i];
354         ny = dp->f[1][0][i] * dp->x + dp->f[1][1][i] * dp->y + dp->f[1][2][i];
355
356
357         switch (dp->variation[i]) {
358                 case 1:
359                         /* sinusoidal */
360                         nx = sin(nx);
361                         ny = sin(ny);
362                         break;
363                 case 2:
364                         {
365                                 /* complex */
366                                 double      r2 = nx * nx + ny * ny + 1e-6;
367
368                                 nx = nx / r2;
369                                 ny = ny / r2;
370                                 break;
371                         }
372                 case 3:
373                         /* bent */
374                         if (nx < 0.0)
375                                 nx = nx * 2.0;
376                         if (ny < 0.0)
377                                 ny = ny / 2.0;
378                         break;
379                 case 4:
380                         {
381                                 /* swirl */
382
383                                 double      r = (nx * nx + ny * ny);    /* times k here is fun */
384                                 double      c1 = sin(r);
385                                 double      c2 = cos(r);
386                                 double      t = nx;
387
388                                 if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
389                                         ny = 1e4;
390                                 else
391                                         ny = c2 * t + c1 * ny;
392                                 nx = c1 * nx - c2 * ny;
393                                 break;
394                         }
395                 case 5:
396                         {
397                                 /* horseshoe */
398                                 double      r, c1, c2, t;
399
400                                 /* Avoid atan2: DOMAIN error message */
401                                 if (nx == 0.0 && ny == 0.0)
402                                         r = 0.0;
403                                 else
404                                         r = atan2(nx, ny);      /* times k here is fun */
405                                 c1 = sin(r);
406                                 c2 = cos(r);
407                                 t = nx;
408
409                                 nx = c1 * nx - c2 * ny;
410                                 ny = c2 * t + c1 * ny;
411                                 break;
412                         }
413                 case 6:
414                         {
415                                 /* drape */
416                                 double      t;
417
418                                 /* Avoid atan2: DOMAIN error message */
419                                 if (nx == 0.0 && ny == 0.0)
420                                         t = 0.0;
421                                 else
422                                         t = atan2(nx, ny) / M_PI;
423
424                                 if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
425                                         ny = 1e4;
426                                 else
427                                         ny = sqrt(nx * nx + ny * ny) - 1.0;
428                                 nx = t;
429                                 break;
430                         }
431         }
432
433 #if 0
434         /* here are some others */
435         {
436                 /* broken */
437                 if (nx > 1.0)
438                         nx = nx - 1.0;
439                 if (nx < -1.0)
440                         nx = nx + 1.0;
441                 if (ny > 1.0)
442                         ny = ny - 1.0;
443                 if (ny < -1.0)
444                         ny = ny + 1.0;
445                 break;
446         }
447         {
448                 /* complex sine */
449                 double      u = nx, v = ny;
450                 double      ev = exp(v);
451                 double      emv = exp(-v);
452
453                 nx = (ev + emv) * sin(u) / 2.0;
454                 ny = (ev - emv) * cos(u) / 2.0;
455         }
456         {
457
458                 /* polynomial */
459                 if (nx < 0)
460                         nx = -nx * nx;
461                 else
462                         nx = nx * nx;
463
464                 if (ny < 0)
465                         ny = -ny * ny;
466                 else
467                         ny = ny * ny;
468         }
469         {
470                 /* spherical */
471                 double      r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
472
473                 nx = nx / r;
474                 ny = ny / r;
475         }
476         {
477                 nx = atan(nx) / M_PI_2
478                         ny = atan(ny) / M_PI_2
479         }
480 #endif
481
482         /* how to check nan too?  some machines don't have finite().
483            don't need to check ny, it'll propogate */
484         if (nx > 1e4 || nx < -1e4) {
485                 nx = halfrandom(1000) / 500.0 - 1.0;
486                 ny = halfrandom(1000) / 500.0 - 1.0;
487                 dp->fuse = FUSE;
488         }
489         dp->x = nx;
490         dp->y = ny;
491         dp->c = nc;
492
493 }
494
495 static void
496 draw(ModeInfo * mi, driftstruct * dp, Drawable d)
497 {
498         Display    *display = MI_DISPLAY(mi);
499         GC          gc = MI_GC(mi);
500         double      x = dp->x;
501         double      y = dp->y;
502         int         fixed_x, fixed_y, npix, c, n;
503
504         if (dp->fuse) {
505                 dp->fuse--;
506                 return;
507         }
508         if (!(x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0))
509                 return;
510
511         fixed_x = (int) ((dp->width / 2) * (x + 1.0));
512         fixed_y = (int) ((dp->height / 2) * (y + 1.0));
513
514         if (!dp->rainbow) {
515
516                 dp->pts[dp->npoints].x = fixed_x;
517                 dp->pts[dp->npoints].y = fixed_y;
518                 dp->npoints++;
519                 if (dp->npoints == MAXBATCH1) {
520                         XSetForeground(display, gc, dp->pixcol);
521                         XDrawPoints(display, d, gc, dp->pts, dp->npoints, CoordModeOrigin);
522                         dp->npoints = 0;
523                 }
524         } else {
525
526                 npix = MI_NPIXELS(mi);
527                 c = (int) (dp->c * npix);
528
529                 if (c < 0)
530                         c = 0;
531                 if (c >= npix)
532                         c = npix - 1;
533                 n = dp->ncpoints[c];
534                 dp->cpts[c][n].x = fixed_x;
535                 dp->cpts[c][n].y = fixed_y;
536                 if (++dp->ncpoints[c] == MAXBATCH2) {
537                         XSetForeground(display, gc, MI_PIXEL(mi, c));
538                         XDrawPoints(display, d, gc, dp->cpts[c],
539                                     dp->ncpoints[c], CoordModeOrigin);
540                         dp->ncpoints[c] = 0;
541                 }
542         }
543 }
544
545 static void
546 draw_flush(ModeInfo * mi, driftstruct * dp, Drawable d)
547 {
548         Display    *display = MI_DISPLAY(mi);
549         GC          gc = MI_GC(mi);
550
551         if (dp->rainbow) {
552                 int         npix = MI_NPIXELS(mi);
553                 int         i;
554
555                 for (i = 0; i < npix; i++) {
556                         if (dp->ncpoints[i]) {
557                                 XSetForeground(display, gc, MI_PIXEL(mi, i));
558                                 XDrawPoints(display, d, gc, dp->cpts[i],
559                                             dp->ncpoints[i], CoordModeOrigin);
560                                 dp->ncpoints[i] = 0;
561                         }
562                 }
563         } else {
564                 if (dp->npoints)
565                         XSetForeground(display, gc, dp->pixcol);
566                 XDrawPoints(display, d, gc, dp->pts,
567                             dp->npoints, CoordModeOrigin);
568                 dp->npoints = 0;
569         }
570 }
571
572
573 void
574 draw_drift(ModeInfo * mi)
575 {
576         Window      window = MI_WINDOW(mi);
577         driftstruct *dp = &drifts[MI_SCREEN(mi)];
578
579         dp->timer = 3000;
580
581         while (dp->timer) {
582                 iter(dp);
583                 draw(mi, dp, window);
584                 if (dp->total_points++ > dp->fractal_len) {
585                         draw_flush(mi, dp, window);
586                         if (0 == --dp->nfractals) {
587 #ifdef STANDALONE
588                           XSync(MI_DISPLAY(mi), False);
589                           sleep(4); /* #### make settable */
590                           erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
591 #endif /* STANDALONE */
592                           initmode(mi, frandom(2));
593                         }
594                         initfractal(mi);
595                 }
596                 dp->timer--;
597         }
598         if (!dp->grow) {
599                 int         i, j, k;
600
601                 draw_flush(mi, dp, window);
602                 if (dp->liss)
603                         dp->liss_time++;
604                 for (i = 0; i < dp->nxforms; i++)
605                         for (j = 0; j < 2; j++)
606                                 for (k = 0; k < 3; k++) {
607                                         if (dp->liss)
608                                                 dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
609                                         else {
610                                                 double      t = dp->f[j][k][i] += dp->df[j][k][i];
611
612                                                 if (t < -1.0 || 1.0 < t)
613                                                         dp->df[j][k][i] *= -1.0;
614                                         }
615                                 }
616         }
617 }
618
619 void
620 release_drift(ModeInfo * mi)
621 {
622         if (drifts != NULL) {
623                 (void) free((void *) drifts);
624                 drifts = NULL;
625         }
626 }
627
628 void
629 refresh_drift(ModeInfo * mi)
630 {
631         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
632 }