7b590563f20c4eb249a2630ced15fd2a2f322612
[xscreensaver] / hacks / flame.c
1 /* xscreensaver, Copyright (c) 1993-2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* This file was ported from xlock for use in xscreensaver (and standalone)
13  * by jwz on 18-Oct-93.  (And again, 11-May-97.)  Original copyright reads:
14  *
15  *   static char sccsid[] = "@(#)flame.c 1.4 91/09/27 XLOCK";
16  *
17  * flame.c - recursive fractal cosmic flames.
18  *
19  * Copyright (c) 1991 by Patrick J. Naughton.
20  *
21  * Permission to use, copy, modify, and distribute this software and its
22  * documentation for any purpose and without fee is hereby granted,
23  * provided that the above copyright notice appear in all copies and that
24  * both that copyright notice and this permission notice appear in
25  * supporting documentation.
26  *
27  * This file is provided AS IS with no warranties of any kind.  The author
28  * shall have no liability with respect to the infringement of copyrights,
29  * trade secrets or any patents by this file or any part thereof.  In no
30  * event will the author be liable for any lost revenue or profits or
31  * other special, indirect and consequential damages.
32  *
33  * Comments and additions should be sent to the author:
34  *
35  *                     naughton@eng.sun.com
36  *
37  *                     Patrick J. Naughton
38  *                     MS 21-14
39  *                     Sun Laboritories, Inc.
40  *                     2550 Garcia Ave
41  *                     Mountain View, CA  94043
42  *
43  * Revision History:
44  * 01-Jun-95: This should look more like the original with some updates by
45  *            Scott Draves.
46  * 27-Jun-91: vary number of functions used.
47  * 24-Jun-91: fixed portability problem with integer mod (%).
48  * 06-Jun-91: Written. (received from Scott Draves, spot@cs.cmu.edu).
49  */
50
51 #include <math.h>
52 #include "screenhack.h"
53
54 #include <signal.h>             /* so we can ignore SIGFPE */
55
56 #define POINT_BUFFER_SIZE 10
57 #define MAXLEV 4
58 #define MAXKINDS  10
59
60 struct state {
61   Display *dpy;
62   Window window;
63
64   double f[2][3][MAXLEV];       /* three non-homogeneous transforms */
65   int max_total;
66   int max_levels;
67   int max_points;
68   int cur_level;
69   int variation;
70   int snum;
71   int anum;
72   int num_points;
73   int total_points;
74   int pixcol;
75   int ncolors;
76   XColor *colors;
77   XPoint points [POINT_BUFFER_SIZE];
78   GC gc;
79
80   int delay, delay2;
81   int width, height;
82
83   short lasthalf;
84
85   int flame_alt;
86   int do_reset;
87 };
88
89
90 static short
91 halfrandom (struct state *st, int mv)
92 {
93   unsigned long r;
94
95   if (st->lasthalf)
96     {
97       r = st->lasthalf;
98       st->lasthalf = 0;
99     }
100   else
101     {
102       r = random ();
103       st->lasthalf = r >> 16;
104     }
105   return (r % mv);
106 }
107
108 static void *
109 flame_init (Display *dpy, Window window)
110 {
111   struct state *st = (struct state *) calloc (1, sizeof(*st));
112   XGCValues gcv;
113   XWindowAttributes xgwa;
114   Colormap cmap;
115
116   st->dpy = dpy;
117   st->window = window;
118
119 #if defined(SIGFPE) && defined(SIG_IGN)
120   /* No doubt a better fix would be to track down where the NaN is coming
121      from, and code around that; but this should do.  Apparently most systems
122      (Linux, Solaris, Irix, ...) ignore FPE by default -- but FreeBSD dumps
123      core by default. */
124   signal (SIGFPE, SIG_IGN);
125 #endif
126
127   XGetWindowAttributes (st->dpy, st->window, &xgwa);
128   st->width = xgwa.width;
129   st->height = xgwa.height;
130   cmap = xgwa.colormap;
131
132   st->max_points = get_integer_resource (st->dpy, "iterations", "Integer");
133   if (st->max_points <= 0) st->max_points = 100;
134
135   st->max_levels = st->max_points;
136
137   st->max_total = get_integer_resource (st->dpy, "points", "Integer");
138   if (st->max_total <= 0) st->max_total = 10000;
139
140   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
141   if (st->delay < 0) st->delay = 0;
142   st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer");
143   if (st->delay2 < 0) st->delay2 = 0;
144
145   st->variation = random() % MAXKINDS;
146
147   if (mono_p)
148     st->ncolors = 0;
149   else
150     {
151       st->ncolors = get_integer_resource (st->dpy, "colors", "Integer");
152       if (st->ncolors <= 0) st->ncolors = 128;
153       st->colors = (XColor *) malloc ((st->ncolors+1) * sizeof (*st->colors));
154       make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap,
155                             st->colors, &st->ncolors,
156                             True, 0, True);
157       if (st->ncolors <= 2)
158         mono_p = True, st->ncolors = 0;
159     }
160
161   gcv.foreground = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
162   gcv.background = get_pixel_resource (st->dpy, cmap, "background", "Background");
163
164   if (! mono_p)
165     {
166       st->pixcol = halfrandom (st, st->ncolors);
167       gcv.foreground = (st->colors [st->pixcol].pixel);
168     }
169
170   st->gc = XCreateGC (st->dpy, st->window, GCForeground | GCBackground, &gcv);
171   return st;
172 }
173
174 static int
175 recurse (struct state *st, double x, double y, int l, Display *dpy, Window win)
176 {
177   int i;
178   double nx, ny;
179
180   if (l == st->max_levels)
181     {
182       st->total_points++;
183       if (st->total_points > st->max_total) /* how long each fractal runs */
184         return 0;
185
186       if (x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0)
187         {
188           st->points[st->num_points].x = (int) ((st->width / 2) * (x + 1.0));
189           st->points[st->num_points].y = (int) ((st->height / 2) * (y + 1.0));
190           st->num_points++;
191           if (st->num_points >= POINT_BUFFER_SIZE)
192             {
193               XDrawPoints (st->dpy, win, st->gc, st->points, st->num_points, CoordModeOrigin);
194               st->num_points = 0;
195             }
196         }
197     }
198   else
199     {
200       for (i = 0; i < st->snum; i++)
201         {
202
203           /* Scale back when values get very large. Spot sez:
204              "I think this happens on HPUX.  I think it's non-IEEE
205              to generate an exception instead of a silent NaN."
206            */
207           if ((fabs(x) > 1.0E5) || (fabs(y) > 1.0E5))
208             x = x / y;
209
210           nx = st->f[0][0][i] * x + st->f[0][1][i] * y + st->f[0][2][i];
211           ny = st->f[1][0][i] * x + st->f[1][1][i] * y + st->f[1][2][i];
212           if (i < st->anum)
213             {
214               switch (st->variation)
215                 {
216                 case 0: /* sinusoidal */
217                   nx = sin(nx);
218                   ny = sin(ny);
219                   break;
220                 case 1: /* complex */
221                   {
222                     double r2 = nx * nx + ny * ny + 1e-6;
223                     nx = nx / r2;
224                     ny = ny / r2;
225                   }
226                   break;
227                 case 2: /* bent */
228                   if (nx < 0.0)
229                     nx = nx * 2.0;
230                   if (ny < 0.0)
231                     ny = ny / 2.0;
232                   break;
233                 case 3: /* swirl */
234                   {
235                     double r = (nx * nx + ny * ny);     /* times k here is fun */
236                     double c1 = sin(r);
237                     double c2 = cos(r);
238                     double t = nx;
239
240                     if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
241                       ny = 1e4;
242                     else
243                       ny = c2 * t + c1 * ny;
244                     nx = c1 * nx - c2 * ny;
245                   }
246                   break;
247                 case 4: /* horseshoe */
248                   {
249                     double r, c1, c2, t;
250
251                     /* Avoid atan2: DOMAIN error message */
252                     if (nx == 0.0 && ny == 0.0)
253                       r = 0.0;
254                     else
255                       r = atan2(nx, ny);      /* times k here is fun */
256                     c1 = sin(r);
257                     c2 = cos(r);
258                     t = nx;
259
260                     nx = c1 * nx - c2 * ny;
261                     ny = c2 * t + c1 * ny;
262                   }
263                   break;
264                 case 5: /* drape */
265                   {
266                     double t;
267
268                     /* Avoid atan2: DOMAIN error message */
269                     if (nx == 0.0 && ny == 0.0)
270                       t = 0.0;
271                     else
272                       t = atan2(nx, ny) / M_PI;
273
274                     if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
275                       ny = 1e4;
276                     else
277                       ny = sqrt(nx * nx + ny * ny) - 1.0;
278                     nx = t;
279                   }
280                   break;
281                 case 6: /* broken */
282                   if (nx > 1.0)
283                     nx = nx - 1.0;
284                   if (nx < -1.0)
285                     nx = nx + 1.0;
286                   if (ny > 1.0)
287                     ny = ny - 1.0;
288                   if (ny < -1.0)
289                     ny = ny + 1.0;
290                   break;
291                 case 7: /* spherical */
292                   {
293                     double r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
294
295                     nx = nx / r;
296                     ny = ny / r;
297                   }
298                   break;
299                 case 8: /*  */
300                   nx = atan(nx) / M_PI_2;
301                   ny = atan(ny) / M_PI_2;
302                   break;
303 /* #if 0 */  /* core dumps on some machines, why not all? */
304                 case 9: /* complex sine */
305                   {
306                     double u = nx;
307                     double v = ny;
308                     double ev = exp(v);
309                     double emv = exp(-v);
310
311                     nx = (ev + emv) * sin(u) / 2.0;
312                     ny = (ev - emv) * cos(u) / 2.0;
313                   }
314                   break;
315                 case 10:        /* polynomial */
316                   if (nx < 0)
317                     nx = -nx * nx;
318                   else
319                     nx = nx * nx;
320                   if (ny < 0)
321                     ny = -ny * ny;
322                   else
323                     ny = ny * ny;
324                   break;
325 /* #endif */
326                 default:
327                   nx = sin(nx);
328                   ny = sin(ny);
329                 }
330             }
331           if (!recurse (st, nx, ny, l + 1, st->dpy, win))
332             return 0;
333         }
334     }
335   return 1;
336 }
337
338 static unsigned long
339 flame_draw (Display *dpy, Window window, void *closure)
340 {
341   struct state *st = (struct state *) closure;
342   int i, j, k;
343   unsigned long this_delay = st->delay;
344
345   if (st->do_reset)
346     {
347       st->do_reset = 0;
348       XClearWindow (st->dpy, st->window);
349     }
350
351   if (!(st->cur_level++ % st->max_levels))
352     {
353       st->do_reset = 1;
354       this_delay = st->delay2;
355       st->flame_alt = !st->flame_alt;
356       st->variation = random() % MAXKINDS;
357     }
358   else
359     {
360       if (st->ncolors > 2)
361         {
362           XSetForeground (st->dpy, st->gc, st->colors [st->pixcol].pixel);
363           if (--st->pixcol < 0)
364             st->pixcol = st->ncolors - 1;
365         }
366     }
367
368   /* number of functions */
369   st->snum = 2 + (st->cur_level % (MAXLEV - 1));
370
371   /* how many of them are of alternate form */
372   if (st->flame_alt)
373     st->anum = 0;
374   else
375     st->anum = halfrandom (st, st->snum) + 2;
376
377   /* 6 coefs per function */
378   for (k = 0; k < st->snum; k++)
379     {
380       for (i = 0; i < 2; i++)
381         for (j = 0; j < 3; j++)
382           st->f[i][j][k] = ((double) (random() & 1023) / 512.0 - 1.0);
383     }
384   st->num_points = 0;
385   st->total_points = 0;
386   recurse (st, 0.0, 0.0, 0, st->dpy, st->window);
387   XDrawPoints (st->dpy, st->window, st->gc, st->points, st->num_points, CoordModeOrigin);
388
389   return this_delay;
390 }
391
392
393 #if defined(__hpux) && defined(PLOSS)
394 /* I don't understand why this is necessary, but I'm told that this program
395    does nothing at all on HP-sUX without it.
396
397    I'm further told that HPUX 11.0 doesn't define PLOSS, and works ok without
398    this section.  Go figure.
399  */
400 #undef random
401 #undef srandom
402 #include <math.h>
403 int matherr(x)
404    register struct exception *x;
405 {
406   if (x->type == PLOSS) return 1;
407   else return 0;
408 }
409 #endif /* __hpux */
410
411
412 \f
413 static const char *flame_defaults [] = {
414   ".background: black",
415   ".foreground: white",
416   "*fpsSolid:   true",
417   "*colors:     64",
418   "*iterations: 25",
419   "*delay:      50000",
420   "*delay2:     2000000",
421   "*points:     10000",
422 #ifdef HAVE_MOBILE
423   "*ignoreRotation: True",
424 #endif
425   0
426 };
427
428 static XrmOptionDescRec flame_options [] = {
429   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
430   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
431   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
432   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
433   { "-points",          ".points",      XrmoptionSepArg, 0 },
434   { 0, 0, 0, 0 }
435 };
436
437 static void
438 flame_reshape (Display *dpy, Window window, void *closure, 
439                  unsigned int w, unsigned int h)
440 {
441   struct state *st = (struct state *) closure;
442   st->width = w;
443   st->height = h;
444 }
445
446 static Bool
447 flame_event (Display *dpy, Window window, void *closure, XEvent *event)
448 {
449   struct state *st = (struct state *) closure;
450   if (screenhack_event_helper (dpy, window, event))
451     {
452       st->do_reset = 1;
453       return True;
454     }
455   return False;
456 }
457
458 static void
459 flame_free (Display *dpy, Window window, void *closure)
460 {
461   struct state *st = (struct state *) closure;
462   free (st);
463 }
464
465 XSCREENSAVER_MODULE ("Flame", flame)
466