http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[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       5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 1991 by Patrick J. Naughton.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Jamie Zawinski <jwz@jwz.org> compatible with xscreensaver
27  * 01-Jan-1997: Moved new flame to drift.  Compile time options now run time.
28  * 01-Jun-1995: Updated by Scott Draves.
29  * 27-Jun-1991: vary number of functions used.
30  * 24-Jun-1991: fixed portability problem with integer mod (%).
31  * 06-Jun-1991: Written, received from Scott Draves <spot@cs.cmu.edu>
32  */
33
34 #ifdef STANDALONE
35 #define MODE_drift
36 #define PROGCLASS "Drift"
37 #define HACK_INIT init_drift
38 #define HACK_DRAW draw_drift
39 #define drift_opts xlockmore_opts
40 #define DEFAULTS "*delay: 10000 \n" \
41  "*count: 30 \n" \
42  "*ncolors: 200 \n"
43 #define SMOOTH_COLORS
44 #include "xlockmore.h"          /* in xscreensaver distribution */
45 #include "erase.h"
46 #else /* STANDALONE */
47 #include "xlock.h"              /* in xlockmore distribution */
48
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         {(char *) "-grow", (char *) ".drift.grow", XrmoptionNoArg, (caddr_t) "on"},
66         {(char *) "+grow", (char *) ".drift.grow", XrmoptionNoArg, (caddr_t) "off"},
67         {(char *) "-liss", (char *) ".drift.trail", XrmoptionNoArg, (caddr_t) "on"},
68         {(char *) "+liss", (char *) ".drift.trail", XrmoptionNoArg, (caddr_t) "off"}
69 };
70 static argtype vars[] =
71 {
72         {(caddr_t *) & grow, (char *) "grow", (char *) "Grow", (char *) DEF_GROW, t_Bool},
73         {(caddr_t *) & liss, (char *) "liss", (char *) "Liss", (char *) DEF_LISS, t_Bool}
74 };
75 static OptionStruct desc[] =
76 {
77         {(char *) "-/+grow", (char *) "turn on/off growing fractals, else they are animated"},
78         {(char *) "-/+liss", (char *) "turn on/off using lissojous figures to get points"}
79 };
80
81 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", "release_drift",
87  "refresh_drift", "init_drift", (char *) NULL, &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 } driftstruct;
138
139 static driftstruct *drifts = (driftstruct *) NULL;
140
141 static short
142 halfrandom(driftstruct * dp, int mv)
143 {
144         unsigned long r;
145
146         if (dp->lasthalf) {
147                 r = dp->lasthalf;
148                 dp->lasthalf = 0;
149         } else {
150                 r = LRAND();
151                 dp->lasthalf = (short) (r >> 16);
152         }
153         r = r % mv;
154         return r;
155 }
156
157 static int
158 frandom(driftstruct * dp, int n)
159 {
160         int         result;
161
162         if (3 > dp->nbits) {
163                 dp->saved_random_bits = LRAND();
164                 dp->nbits = 31;
165         }
166         switch (n) {
167                 case 2:
168                         result = (int) (dp->saved_random_bits & 1);
169                         dp->saved_random_bits >>= 1;
170                         dp->nbits -= 1;
171                         return result;
172
173                 case 3:
174                         result = (int) (dp->saved_random_bits & 3);
175                         dp->saved_random_bits >>= 2;
176                         dp->nbits -= 2;
177                         if (3 == result)
178                                 return frandom(dp, 3);
179                         return result;
180
181                 case 4:
182                         result = (int) (dp->saved_random_bits & 3);
183                         dp->saved_random_bits >>= 2;
184                         dp->nbits -= 2;
185                         return result;
186
187                 case 5:
188                         result = (int) (dp->saved_random_bits & 7);
189                         dp->saved_random_bits >>= 3;
190                         dp->nbits -= 3;
191                         if (4 < result)
192                                 return frandom(dp, 5);
193                         return result;
194                 default:
195                         (void) fprintf(stderr, "bad arg to frandom\n");
196         }
197         return 0;
198 }
199
200 #define DISTRIB_A (halfrandom(dp, 7000) + 9000)
201 #define DISTRIB_B ((frandom(dp, 3) + 1) * (frandom(dp, 3) + 1) * 120000)
202 #define LEN(x) (sizeof(x)/sizeof((x)[0]))
203
204 static void
205 initmode(ModeInfo * mi, int mode)
206 {
207         driftstruct *dp = &drifts[MI_SCREEN(mi)];
208
209 #define VARIATION_LEN 14
210
211         dp->mode = mode;
212
213         dp->major_variation = halfrandom(dp, VARIATION_LEN);
214         /*  0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 6 */
215         dp->major_variation = ((dp->major_variation >= VARIATION_LEN >> 1) &&
216                                (dp->major_variation < VARIATION_LEN - 1)) ?
217                 (dp->major_variation + 1) >> 1 : dp->major_variation >> 1;
218
219         if (dp->grow) {
220                 dp->rainbow = 0;
221                 if (mode) {
222                         if (!dp->color || halfrandom(dp, 8)) {
223                                 dp->nfractals = halfrandom(dp, 30) + 5;
224                                 dp->fractal_len = DISTRIB_A;
225                         } else {
226                                 dp->nfractals = halfrandom(dp, 5) + 5;
227                                 dp->fractal_len = DISTRIB_B;
228                         }
229                 } else {
230                         dp->rainbow = dp->color;
231                         dp->nfractals = 1;
232                         dp->fractal_len = DISTRIB_B;
233                 }
234         } else {
235                 dp->nfractals = 1;
236                 dp->rainbow = dp->color;
237                 dp->fractal_len = 2000000;
238         }
239         dp->fractal_len = (dp->fractal_len * MI_COUNT(mi)) / 20;
240
241         MI_CLEARWINDOW(mi);
242 }
243
244 static void
245 pick_df_coefs(ModeInfo * mi)
246 {
247         driftstruct *dp = &drifts[MI_SCREEN(mi)];
248         int         i, j, k;
249         double      r;
250
251         for (i = 0; i < dp->nxforms; i++) {
252
253                 r = 1e-6;
254                 for (j = 0; j < 2; j++)
255                         for (k = 0; k < 3; k++) {
256                                 dp->df[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
257                                 r += dp->df[j][k][i] * dp->df[j][k][i];
258                         }
259                 r = (3 + halfrandom(dp, 5)) * 0.01 / sqrt(r);
260                 for (j = 0; j < 2; j++)
261                         for (k = 0; k < 3; k++)
262                                 dp->df[j][k][i] *= r;
263         }
264 }
265
266 static void
267 free_drift(driftstruct *dp)
268 {
269         if (dp->ncpoints != NULL) {
270                 (void) free((void *) dp->ncpoints);
271                 dp->ncpoints = (int *) NULL;
272         }
273         if (dp->cpts != NULL) {
274                 (void) free((void *) dp->cpts);
275                 dp->cpts = (XPoint *) NULL;
276         }
277 }
278
279 static void
280 initfractal(ModeInfo * mi)
281 {
282         driftstruct *dp = &drifts[MI_SCREEN(mi)];
283         int         i, j, k;
284
285 #define XFORM_LEN 9
286
287         dp->fuse = FUSE;
288         dp->total_points = 0;
289
290         if (!dp->ncpoints) {
291                 if ((dp->ncpoints = (int *) malloc(sizeof (int) * MI_NCOLORS(mi))) ==
292                         NULL) {
293                         free_drift(dp);
294                         return;
295                 }
296         }
297         if (!dp->cpts) {
298                 if ((dp->cpts = (XPoint *) malloc(MAXBATCH2 * sizeof (XPoint) *
299                          MI_NCOLORS(mi))) == NULL) {
300                         free_drift(dp);
301                         return;
302                 }
303         }
304
305         if (dp->rainbow)
306                 for (i = 0; i < MI_NPIXELS(mi); i++)
307                         dp->ncpoints[i] = 0;
308         else
309                 dp->npoints = 0;
310         dp->nxforms = halfrandom(dp, XFORM_LEN);
311         /* 2, 2, 2, 3, 3, 3, 4, 4, 5 */
312         dp->nxforms = (dp->nxforms >= XFORM_LEN - 1) + dp->nxforms / 3 + 2;
313
314         dp->c = dp->x = dp->y = 0.0;
315         if (dp->liss && !halfrandom(dp, 10)) {
316                 dp->liss_time = 0;
317         }
318         if (!dp->grow)
319                 pick_df_coefs(mi);
320         for (i = 0; i < dp->nxforms; i++) {
321                 if (NMAJORVARS == dp->major_variation)
322                         dp->variation[i] = halfrandom(dp, NMAJORVARS);
323                 else
324                         dp->variation[i] = dp->major_variation;
325                 for (j = 0; j < 2; j++)
326                         for (k = 0; k < 3; k++) {
327                                 if (dp->liss)
328                                         dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
329                                 else
330                                         dp->f[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
331                         }
332         }
333         if (dp->color)
334                 dp->pixcol = MI_PIXEL(mi, halfrandom(dp, MI_NPIXELS(mi)));
335         else
336                 dp->pixcol = MI_WHITE_PIXEL(mi);
337
338 }
339
340
341 void
342 init_drift(ModeInfo * mi)
343 {
344         driftstruct *dp;
345
346         if (drifts == NULL) {
347                 if ((drifts = (driftstruct *) calloc(MI_NUM_SCREENS(mi),
348                                               sizeof (driftstruct))) == NULL)
349                         return;
350         }
351         dp = &drifts[MI_SCREEN(mi)];
352
353         dp->width = MI_WIDTH(mi);
354         dp->height = MI_HEIGHT(mi);
355         dp->color = MI_NPIXELS(mi) > 2;
356
357         if (MI_IS_FULLRANDOM(mi)) {
358                 if (NRAND(3) == 0)
359                         dp->grow = True;
360                 else {
361                         dp->grow = False;
362                         dp->liss = (Bool) (LRAND() & 1);
363                 }
364         } else {
365                 dp->grow = grow;
366                 if (dp->grow)
367                         dp->liss = False;
368                 else
369                         dp->liss = liss;
370         }
371         initmode(mi, 1);
372         initfractal(mi);
373 }
374
375 static void
376 iter(driftstruct * dp)
377 {
378         int         i = frandom(dp, dp->nxforms);
379         double      nx, ny, nc;
380
381
382         if (i)
383                 nc = (dp->c + 1.0) / 2.0;
384         else
385                 nc = dp->c / 2.0;
386
387         nx = dp->f[0][0][i] * dp->x + dp->f[0][1][i] * dp->y + dp->f[0][2][i];
388         ny = dp->f[1][0][i] * dp->x + dp->f[1][1][i] * dp->y + dp->f[1][2][i];
389
390
391         switch (dp->variation[i]) {
392                 case 1:
393                         /* sinusoidal */
394                         nx = sin(nx);
395                         ny = sin(ny);
396                         break;
397                 case 2:
398                         {
399                                 /* complex */
400                                 double      r2 = nx * nx + ny * ny + 1e-6;
401
402                                 nx = nx / r2;
403                                 ny = ny / r2;
404                                 break;
405                         }
406                 case 3:
407                         /* bent */
408                         if (nx < 0.0)
409                                 nx = nx * 2.0;
410                         if (ny < 0.0)
411                                 ny = ny / 2.0;
412                         break;
413                 case 4:
414                         {
415                                 /* swirl */
416
417                                 double      r = (nx * nx + ny * ny);    /* times k here is fun */
418                                 double      c1 = sin(r);
419                                 double      c2 = cos(r);
420                                 double      t = nx;
421
422                                 if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
423                                         ny = 1e4;
424                                 else
425                                         ny = c2 * t + c1 * ny;
426                                 nx = c1 * nx - c2 * ny;
427                                 break;
428                         }
429                 case 5:
430                         {
431                                 /* horseshoe */
432                                 double      r, c1, c2, t;
433
434                                 /* Avoid atan2: DOMAIN error message */
435                                 if (nx == 0.0 && ny == 0.0)
436                                         r = 0.0;
437                                 else
438                                         r = atan2(nx, ny);      /* times k here is fun */
439                                 c1 = sin(r);
440                                 c2 = cos(r);
441                                 t = nx;
442
443                                 nx = c1 * nx - c2 * ny;
444                                 ny = c2 * t + c1 * ny;
445                                 break;
446                         }
447                 case 6:
448                         {
449                                 /* drape */
450                                 double      t;
451
452                                 /* Avoid atan2: DOMAIN error message */
453                                 if (nx == 0.0 && ny == 0.0)
454                                         t = 0.0;
455                                 else
456                                         t = atan2(nx, ny) / M_PI;
457
458                                 if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
459                                         ny = 1e4;
460                                 else
461                                         ny = sqrt(nx * nx + ny * ny) - 1.0;
462                                 nx = t;
463                                 break;
464                         }
465         }
466
467 #if 0
468         /* here are some others */
469         {
470                 /* broken */
471                 if (nx > 1.0)
472                         nx = nx - 1.0;
473                 if (nx < -1.0)
474                         nx = nx + 1.0;
475                 if (ny > 1.0)
476                         ny = ny - 1.0;
477                 if (ny < -1.0)
478                         ny = ny + 1.0;
479                 break;
480         }
481         {
482                 /* complex sine */
483                 double      u = nx, v = ny;
484                 double      ev = exp(v);
485                 double      emv = exp(-v);
486
487                 nx = (ev + emv) * sin(u) / 2.0;
488                 ny = (ev - emv) * cos(u) / 2.0;
489         }
490         {
491
492                 /* polynomial */
493                 if (nx < 0)
494                         nx = -nx * nx;
495                 else
496                         nx = nx * nx;
497
498                 if (ny < 0)
499                         ny = -ny * ny;
500                 else
501                         ny = ny * ny;
502         }
503         {
504                 /* spherical */
505                 double      r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
506
507                 nx = nx / r;
508                 ny = ny / r;
509         }
510         {
511                 nx = atan(nx) / M_PI_2
512                         ny = atan(ny) / M_PI_2
513         }
514 #endif
515
516         /* how to check nan too?  some machines don't have finite().
517            don't need to check ny, it'll propogate */
518         if (nx > 1e4 || nx < -1e4) {
519                 nx = halfrandom(dp, 1000) / 500.0 - 1.0;
520                 ny = halfrandom(dp, 1000) / 500.0 - 1.0;
521                 dp->fuse = FUSE;
522         }
523         dp->x = nx;
524         dp->y = ny;
525         dp->c = nc;
526
527 }
528
529 static void
530 draw(ModeInfo * mi, driftstruct * dp, Drawable d)
531 {
532         Display    *display = MI_DISPLAY(mi);
533         GC          gc = MI_GC(mi);
534         double      x = dp->x;
535         double      y = dp->y;
536         int         fixed_x, fixed_y, npix, c, n;
537
538         if (dp->fuse) {
539                 dp->fuse--;
540                 return;
541         }
542         if (!(x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0))
543                 return;
544
545         fixed_x = (int) ((dp->width / 2) * (x + 1.0));
546         fixed_y = (int) ((dp->height / 2) * (y + 1.0));
547
548         if (!dp->rainbow) {
549
550                 dp->pts[dp->npoints].x = fixed_x;
551                 dp->pts[dp->npoints].y = fixed_y;
552                 dp->npoints++;
553                 if (dp->npoints == MAXBATCH1) {
554                         XSetForeground(display, gc, dp->pixcol);
555                         XDrawPoints(display, d, gc, dp->pts, dp->npoints, CoordModeOrigin);
556                         dp->npoints = 0;
557                 }
558         } else {
559
560                 npix = MI_NPIXELS(mi);
561                 c = (int) (dp->c * npix);
562
563                 if (c < 0)
564                         c = 0;
565                 if (c >= npix)
566                         c = npix - 1;
567                 n = dp->ncpoints[c];
568                 dp->cpts[c * MAXBATCH2 + n].x = fixed_x;
569                 dp->cpts[c * MAXBATCH2 + n].y = fixed_y;
570                 if (++dp->ncpoints[c] == MAXBATCH2) {
571                         XSetForeground(display, gc, MI_PIXEL(mi, c));
572                         XDrawPoints(display, d, gc, &(dp->cpts[c * MAXBATCH2]),
573                                     dp->ncpoints[c], CoordModeOrigin);
574                         dp->ncpoints[c] = 0;
575                 }
576         }
577 }
578
579 static void
580 draw_flush(ModeInfo * mi, driftstruct * dp, Drawable d)
581 {
582         Display    *display = MI_DISPLAY(mi);
583         GC          gc = MI_GC(mi);
584
585         if (dp->rainbow) {
586                 int         npix = MI_NPIXELS(mi);
587                 int         i;
588
589                 for (i = 0; i < npix; i++) {
590                         if (dp->ncpoints[i]) {
591                                 XSetForeground(display, gc, MI_PIXEL(mi, i));
592                                 XDrawPoints(display, d, gc, &(dp->cpts[i * MAXBATCH2]),
593                                             dp->ncpoints[i], CoordModeOrigin);
594                                 dp->ncpoints[i] = 0;
595                         }
596                 }
597         } else {
598                 if (dp->npoints)
599                         XSetForeground(display, gc, dp->pixcol);
600                 XDrawPoints(display, d, gc, dp->pts,
601                             dp->npoints, CoordModeOrigin);
602                 dp->npoints = 0;
603         }
604 }
605
606
607 void
608 draw_drift(ModeInfo * mi)
609 {
610         Window      window = MI_WINDOW(mi);
611         driftstruct *dp;
612
613         if (drifts == NULL)
614                 return;
615         dp = &drifts[MI_SCREEN(mi)];
616         if (dp->ncpoints == NULL)
617                 return;
618
619         MI_IS_DRAWN(mi) = True;
620         dp->timer = 3000;
621         while (dp->timer) {
622                 iter(dp);
623                 draw(mi, dp, window);
624                 if (dp->total_points++ > dp->fractal_len) {
625                         draw_flush(mi, dp, window);
626                         if (0 == --dp->nfractals) {
627 #ifdef STANDALONE
628                           XSync(MI_DISPLAY(mi), False);
629                           sleep(4); /* #### make settable */
630                           erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
631 #endif /* STANDALONE */
632                                 initmode(mi, frandom(dp, 2));
633                         }
634                         initfractal(mi);
635                 }
636                 dp->timer--;
637         }
638         if (!dp->grow) {
639                 int         i, j, k;
640
641                 draw_flush(mi, dp, window);
642                 if (dp->liss)
643                         dp->liss_time++;
644                 for (i = 0; i < dp->nxforms; i++)
645                         for (j = 0; j < 2; j++)
646                                 for (k = 0; k < 3; k++) {
647                                         if (dp->liss)
648                                                 dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
649                                         else {
650                                                 double      t = dp->f[j][k][i] += dp->df[j][k][i];
651
652                                                 if (t < -1.0 || 1.0 < t)
653                                                         dp->df[j][k][i] *= -1.0;
654                                         }
655                                 }
656         }
657 }
658
659 void
660 release_drift(ModeInfo * mi)
661 {
662         if (drifts != NULL) {
663                 int         screen;
664
665                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
666                         free_drift(&drifts[screen]);
667                 (void) free((void *) drifts);
668                 drifts = (driftstruct *) NULL;
669         }
670 }
671
672 void
673 refresh_drift(ModeInfo * mi)
674 {
675         MI_CLEARWINDOW(mi);
676 }
677
678 #endif /* MODE_drift */