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