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