ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[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 PROGCLASS "Drift"
36 #define HACK_INIT init_drift
37 #define HACK_DRAW draw_drift
38 #define drift_opts xlockmore_opts
39 #define DEFAULTS "*delay: 10000 \n" \
40  "*count: 30 \n" \
41  "*ncolors: 200 \n"
42 #define SMOOTH_COLORS
43 #include "xlockmore.h"          /* in xscreensaver distribution */
44 #include "erase.h"
45 #else /* STANDALONE */
46 #include "xlock.h"              /* in xlockmore distribution */
47
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, "liss", "Liss", 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 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 } driftstruct;
137
138 static driftstruct *drifts = (driftstruct *) NULL;
139
140 static short
141 halfrandom(driftstruct * dp, int mv)
142 {
143         unsigned long r;
144
145         if (dp->lasthalf) {
146                 r = dp->lasthalf;
147                 dp->lasthalf = 0;
148         } else {
149                 r = LRAND();
150                 dp->lasthalf = (short) (r >> 16);
151         }
152         r = r % mv;
153         return r;
154 }
155
156 static int
157 frandom(driftstruct * dp, int n)
158 {
159         int         result;
160
161         if (3 > dp->nbits) {
162                 dp->saved_random_bits = LRAND();
163                 dp->nbits = 31;
164         }
165         switch (n) {
166                 case 2:
167                         result = (int) (dp->saved_random_bits & 1);
168                         dp->saved_random_bits >>= 1;
169                         dp->nbits -= 1;
170                         return result;
171
172                 case 3:
173                         result = (int) (dp->saved_random_bits & 3);
174                         dp->saved_random_bits >>= 2;
175                         dp->nbits -= 2;
176                         if (3 == result)
177                                 return frandom(dp, 3);
178                         return result;
179
180                 case 4:
181                         result = (int) (dp->saved_random_bits & 3);
182                         dp->saved_random_bits >>= 2;
183                         dp->nbits -= 2;
184                         return result;
185
186                 case 5:
187                         result = (int) (dp->saved_random_bits & 7);
188                         dp->saved_random_bits >>= 3;
189                         dp->nbits -= 3;
190                         if (4 < result)
191                                 return frandom(dp, 5);
192                         return result;
193                 default:
194                         (void) fprintf(stderr, "bad arg to frandom\n");
195         }
196         return 0;
197 }
198
199 #define DISTRIB_A (halfrandom(dp, 7000) + 9000)
200 #define DISTRIB_B ((frandom(dp, 3) + 1) * (frandom(dp, 3) + 1) * 120000)
201 #define LEN(x) (sizeof(x)/sizeof((x)[0]))
202
203 static void
204 initmode(ModeInfo * mi, int mode)
205 {
206         driftstruct *dp = &drifts[MI_SCREEN(mi)];
207
208 #define VARIATION_LEN 14
209
210         dp->mode = mode;
211
212         dp->major_variation = halfrandom(dp, VARIATION_LEN);
213         /*  0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 6 */
214         dp->major_variation = ((dp->major_variation >= VARIATION_LEN >> 1) &&
215                                (dp->major_variation < VARIATION_LEN - 1)) ?
216                 (dp->major_variation + 1) >> 1 : dp->major_variation >> 1;
217
218         if (dp->grow) {
219                 dp->rainbow = 0;
220                 if (mode) {
221                         if (!dp->color || halfrandom(dp, 8)) {
222                                 dp->nfractals = halfrandom(dp, 30) + 5;
223                                 dp->fractal_len = DISTRIB_A;
224                         } else {
225                                 dp->nfractals = halfrandom(dp, 5) + 5;
226                                 dp->fractal_len = DISTRIB_B;
227                         }
228                 } else {
229                         dp->rainbow = dp->color;
230                         dp->nfractals = 1;
231                         dp->fractal_len = DISTRIB_B;
232                 }
233         } else {
234                 dp->nfractals = 1;
235                 dp->rainbow = dp->color;
236                 dp->fractal_len = 2000000;
237         }
238         dp->fractal_len = (dp->fractal_len * MI_COUNT(mi)) / 20;
239
240         MI_CLEARWINDOW(mi);
241 }
242
243 static void
244 pick_df_coefs(ModeInfo * mi)
245 {
246         driftstruct *dp = &drifts[MI_SCREEN(mi)];
247         int         i, j, k;
248         double      r;
249
250         for (i = 0; i < dp->nxforms; i++) {
251
252                 r = 1e-6;
253                 for (j = 0; j < 2; j++)
254                         for (k = 0; k < 3; k++) {
255                                 dp->df[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
256                                 r += dp->df[j][k][i] * dp->df[j][k][i];
257                         }
258                 r = (3 + halfrandom(dp, 5)) * 0.01 / sqrt(r);
259                 for (j = 0; j < 2; j++)
260                         for (k = 0; k < 3; k++)
261                                 dp->df[j][k][i] *= r;
262         }
263 }
264
265 static void
266 free_drift(driftstruct *dp)
267 {
268         if (dp->ncpoints != NULL) {
269                 (void) free((void *) dp->ncpoints);
270                 dp->ncpoints = (int *) NULL;
271         }
272         if (dp->cpts != NULL) {
273                 (void) free((void *) dp->cpts);
274                 dp->cpts = (XPoint *) NULL;
275         }
276 }
277
278 static void
279 initfractal(ModeInfo * mi)
280 {
281         driftstruct *dp = &drifts[MI_SCREEN(mi)];
282         int         i, j, k;
283
284 #define XFORM_LEN 9
285
286         dp->fuse = FUSE;
287         dp->total_points = 0;
288
289         if (!dp->ncpoints) {
290                 if ((dp->ncpoints = (int *) malloc(sizeof (int) * MI_NCOLORS(mi))) ==
291                         NULL) {
292                         free_drift(dp);
293                         return;
294                 }
295         }
296         if (!dp->cpts) {
297                 if ((dp->cpts = (XPoint *) malloc(MAXBATCH2 * sizeof (XPoint) *
298                          MI_NCOLORS(mi))) == NULL) {
299                         free_drift(dp);
300                         return;
301                 }
302         }
303
304         if (dp->rainbow)
305                 for (i = 0; i < MI_NPIXELS(mi); i++)
306                         dp->ncpoints[i] = 0;
307         else
308                 dp->npoints = 0;
309         dp->nxforms = halfrandom(dp, XFORM_LEN);
310         /* 2, 2, 2, 3, 3, 3, 4, 4, 5 */
311         dp->nxforms = (dp->nxforms >= XFORM_LEN - 1) + dp->nxforms / 3 + 2;
312
313         dp->c = dp->x = dp->y = 0.0;
314         if (dp->liss && !halfrandom(dp, 10)) {
315                 dp->liss_time = 0;
316         }
317         if (!dp->grow)
318                 pick_df_coefs(mi);
319         for (i = 0; i < dp->nxforms; i++) {
320                 if (NMAJORVARS == dp->major_variation)
321                         dp->variation[i] = halfrandom(dp, NMAJORVARS);
322                 else
323                         dp->variation[i] = dp->major_variation;
324                 for (j = 0; j < 2; j++)
325                         for (k = 0; k < 3; k++) {
326                                 if (dp->liss)
327                                         dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
328                                 else
329                                         dp->f[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
330                         }
331         }
332         if (dp->color)
333                 dp->pixcol = MI_PIXEL(mi, halfrandom(dp, MI_NPIXELS(mi)));
334         else
335                 dp->pixcol = MI_WHITE_PIXEL(mi);
336
337 }
338
339
340 void
341 init_drift(ModeInfo * mi)
342 {
343         driftstruct *dp;
344
345         if (drifts == NULL) {
346                 if ((drifts = (driftstruct *) calloc(MI_NUM_SCREENS(mi),
347                                               sizeof (driftstruct))) == NULL)
348                         return;
349         }
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 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         MI_IS_DRAWN(mi) = True;
619         dp->timer = 3000;
620         while (dp->timer) {
621                 iter(dp);
622                 draw(mi, dp, window);
623                 if (dp->total_points++ > dp->fractal_len) {
624                         draw_flush(mi, dp, window);
625                         if (0 == --dp->nfractals) {
626 #ifdef STANDALONE
627                           XSync(MI_DISPLAY(mi), False);
628                           sleep(4); /* #### make settable */
629                           erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
630 #endif /* STANDALONE */
631                                 initmode(mi, frandom(dp, 2));
632                         }
633                         initfractal(mi);
634                 }
635                 dp->timer--;
636         }
637         if (!dp->grow) {
638                 int         i, j, k;
639
640                 draw_flush(mi, dp, window);
641                 if (dp->liss)
642                         dp->liss_time++;
643                 for (i = 0; i < dp->nxforms; i++)
644                         for (j = 0; j < 2; j++)
645                                 for (k = 0; k < 3; k++) {
646                                         if (dp->liss)
647                                                 dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
648                                         else {
649                                                 double      t = dp->f[j][k][i] += dp->df[j][k][i];
650
651                                                 if (t < -1.0 || 1.0 < t)
652                                                         dp->df[j][k][i] *= -1.0;
653                                         }
654                                 }
655         }
656 }
657
658 void
659 release_drift(ModeInfo * mi)
660 {
661         if (drifts != NULL) {
662                 int         screen;
663
664                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
665                         free_drift(&drifts[screen]);
666                 (void) free((void *) drifts);
667                 drifts = (driftstruct *) NULL;
668         }
669 }
670
671 void
672 refresh_drift(ModeInfo * mi)
673 {
674         MI_CLEARWINDOW(mi);
675 }
676
677 #endif /* MODE_drift */