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