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