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