From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / ifs.c
1 /* Copyright © Chris Le Sueur and Robby Griffin, 2005-2006
2
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
10
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21
22 Ultimate thanks go to Massimino Pascal, who created the original
23 xscreensaver hack, and inspired me with it's swirly goodness. This
24 version adds things like variable quality, number of functions and also
25 a groovier colouring mode.
26
27 This version by Chris Le Sueur <thefishface@gmail.com>, Feb 2005
28 Many improvements by Robby Griffin <rmg@terc.edu>, Mar 2006
29 Multi-coloured mode added by Jack Grahl <j.grahl@ucl.ac.uk>, Jan 2007
30 */
31
32 #include <assert.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <math.h>
37
38 #include "screenhack.h"
39
40 #undef countof
41 #define countof(x) (sizeof((x)) / sizeof(*(x)))
42
43 typedef struct {
44   float r, s, tx, ty;   /* Rotation, Scale, Translation X & Y */
45   float ro, rt, rc;     /* Old Rotation, Rotation Target, Rotation Counter */
46   float so, st, sc;     /* Old Scale, Scale Target, Scale Counter */
47   float sa, txa, tya;   /* Scale change, Translation change */
48
49   int ua, ub, utx;      /* Precomputed combined r,s,t values */
50   int uc, ud, uty;      /* Precomputed combined r,s,t values */
51
52 } Lens; 
53
54 struct state {
55   Display *dpy;
56   Window window;
57   GC gc;
58   Drawable backbuffer;
59   XColor *colours;
60   int ncolours;
61   int ccolour;
62   int blackColor, whiteColor;
63
64   int width, widthb, height;
65   int width8, height8;
66   unsigned int *board;
67   XPoint pointbuf[1000];
68   int npoints;
69   int xmin, xmax, ymin, ymax;
70   int x, y;
71
72   int delay;
73
74   int lensnum;
75   Lens *lenses;
76   int length;
77   int mode;
78   Bool recurse;
79   Bool multi;
80   Bool translate, scale, rotate;
81 };
82
83 #define getdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] &  (1<<((x) & 31)))
84 #define setdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] |= (1<<((x) & 31)))
85
86 static float
87 myrandom(float up)
88 {
89   return (((float)random() / RAND_MAX) * up);
90 }
91
92 static const char *ifs_defaults [] = {
93   ".background:         Black",
94   "*lensnum:            3",
95   "*fpsSolid:           true",
96   "*length:             9",
97   "*mode:               0",
98   "*colors:             200",
99   "*delay:              20000",
100   "*translate:          True",
101   "*scale:              True",
102   "*rotate:             True",
103   "*recurse:            False",
104   "*multi:              True",
105 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
106   "*doubleBuffer:       False",
107 #else
108   "*doubleBuffer:       True",
109 #endif
110 #ifdef USE_IPHONE
111   "*ignoreRotation:     True",
112 #endif
113   0
114 };
115
116 static XrmOptionDescRec ifs_options [] = {
117   { "-detail",          ".length",      XrmoptionSepArg, 0 },
118   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
119   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
120   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
121   { "-functions",       ".lensnum",     XrmoptionSepArg, 0 },
122   { "-no-translate",    ".translate",   XrmoptionNoArg, "False" },
123   { "-no-scale",        ".scale",       XrmoptionNoArg, "False" },
124   { "-no-rotate",       ".rotate",      XrmoptionNoArg, "False" },
125   { "-recurse",         ".recurse",     XrmoptionNoArg, "True" },
126   { "-iterate",         ".recurse",     XrmoptionNoArg, "False" },
127   { "-multi",           ".multi",       XrmoptionNoArg, "True" },
128   { "-no-multi",        ".multi",       XrmoptionNoArg, "False" },
129   { "-db",              ".doubleBuffer",XrmoptionNoArg, "True" },
130   { "-no-db",           ".doubleBuffer",XrmoptionNoArg, "False" },
131   { 0, 0, 0, 0 }
132 };
133
134
135 /* Draw all the queued points on the backbuffer */
136 static void
137 drawpoints(struct state *st)
138 {
139   XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
140               CoordModeOrigin);
141   st->npoints = 0;
142 }
143
144 /* Set a point to be drawn, if it hasn't been already.
145  * Expects coordinates in 256ths of a pixel. */
146 static void
147 sp(struct state *st, int x, int y)
148 {
149   if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
150     return;
151
152   x >>= 8;
153   y >>= 8;
154
155   if (getdot(x, y)) return;
156   setdot(x, y);
157
158   if (x < st->xmin) st->xmin = x;
159   if (x > st->xmax) st->xmax = x;
160   if (y < st->ymin) st->ymin = y;
161   if (y > st->ymax) st->ymax = y;
162
163   st->pointbuf[st->npoints].x = x;
164   st->pointbuf[st->npoints].y = y;
165   st->npoints++;
166
167   if (st->npoints >= countof(st->pointbuf)) {
168     drawpoints(st);
169   }
170 }
171
172
173 /* Precompute integer values for matrix multiplication and vector
174  * addition. The matrix multiplication will go like this (see iterate()):
175  *   |x2|     |ua ub|   |x|     |utx|
176  *   |  |  =  |     | * | |  +  |   |
177  *   |y2|     |uc ud|   |y|     |uty|
178  * 
179  * There is an extra factor of 2^10 in these values, and an extra factor of
180  * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
181  */
182 static void
183 lensmatrix(struct state *st, Lens *l)
184 {
185   l->ua = 1024.0 * l->s * cos(l->r);
186   l->ub = -1024.0 * l->s * sin(l->r);
187   l->uc = -l->ub;
188   l->ud = l->ua;
189   l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
190                                    + l->tx / 16 + 1);
191   l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
192                                      + l->ty / 16 - 1);
193 }
194
195 static void
196 CreateLens(struct state *st,
197            float nr,
198            float ns,
199            float nx,
200            float ny,
201            Lens *newlens)
202 {
203   newlens->sa = newlens->txa = newlens->tya = 0;
204   if (st->rotate) {
205     newlens->r = newlens->ro = newlens->rt = nr;
206     newlens->rc = 1;
207   }
208   else newlens->r = 0;
209
210   if (st->scale) {
211     newlens->s = newlens->so = newlens->st = ns;
212     newlens->sc = 1;
213   }
214   else newlens->s = 0.5;
215
216   newlens->tx = nx;
217   newlens->ty = ny;
218
219   lensmatrix(st, newlens);
220 }
221         
222 static void
223 mutate(struct state *st, Lens *l)
224 {
225   if (st->rotate) {
226     float factor;
227     if(l->rc >= 1) {
228       l->rc = 0;
229       l->ro = l->rt;
230       l->rt = myrandom(4) - 2;
231     }
232     factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
233     l->r = l->ro + (l->rt - l->ro) * factor;
234     l->rc += 0.01;
235   }
236   if (st->scale) {
237     float factor;
238     if (l->sc >= 1) {
239       /* Reset counter, obtain new target value */
240       l->sc = 0;
241       l->so = l->st;
242       l->st = myrandom(2) - 1;
243     }
244     factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
245     /* Take average of old target and new target, using factor to *
246      * weight. It's computed sinusoidally, resulting in smooth,   *
247      * rhythmic transitions.                                      */
248     l->s = l->so + (l->st - l->so) * factor;
249     l->sc += 0.01;
250   }
251   if (st->translate) {
252     l->txa += myrandom(0.004) - 0.002;
253     l->tya += myrandom(0.004) - 0.002;
254     l->tx += l->txa;
255     l->ty += l->tya;
256     if (l->tx > 6) l->txa -= 0.004;
257     if (l->ty > 6) l->tya -= 0.004;
258     if (l->tx < -6) l->txa += 0.004;
259     if (l->ty < -6) l->tya += 0.004;
260     if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
261     if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
262   }
263   if (st->rotate || st->scale || st->translate) {
264     lensmatrix(st, l);
265   }
266 }
267
268
269 #define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
270 #define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
271 /*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
272
273 /* Calls itself <lensnum> times - with results from each lens/function.  *
274  * After <length> calls to itself, it stops iterating and draws a point. */
275 static void
276 recurse(struct state *st, int x, int y, int length, int p)
277 {
278   int i;
279   Lens *l;
280
281   if (length == 0) {
282     if (p == 0) 
283       sp(st, x, y);
284     else {
285       l = &st->lenses[p];
286       sp(st, STEPX(l, x, y), STEPY(l, x, y));
287     }
288   }
289   else {
290     for (i = 0; i < st->lensnum; i++) {
291       l = &st->lenses[i];
292       recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
293     }
294   }
295 }
296
297 /* Performs <count> random lens transformations, drawing a point at each
298  * iteration after the first 10.
299  */
300 static void
301 iterate(struct state *st, int count, int p)
302 {
303   int i;
304   Lens *l;
305   int x = st->x;
306   int y = st->y;
307   int tx;
308
309 # define STEP()                              \
310     l = &st->lenses[random() % st->lensnum]; \
311     tx = STEPX(l, x, y);                     \
312     y = STEPY(l, x, y);                      \
313     x = tx
314
315   for (i = 0; i < 10; i++) {
316     STEP();
317   }
318
319   for ( ; i < count; i++) {
320     STEP();
321     if (p == 0)
322       sp(st, x, y);
323     else
324       {
325         l = &st->lenses[p];
326         sp(st, STEPX(l, x, y), STEPY(l, x, y));
327       }
328   }
329
330 # undef STEP
331
332   st->x = x;
333   st->y = y;
334 }
335
336 /* Come on and iterate, iterate, iterate and sing... *
337  * Yeah, this function just calls iterate, mutate,   *
338  * and then draws everything.                        */
339 static unsigned long
340 ifs_draw (Display *dpy, Window window, void *closure)
341 {
342   struct state *st = (struct state *) closure;
343   int i;
344   int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
345   int partcolor, x, y;
346   
347
348   /* erase whatever was drawn in the previous frame */
349   if (xmin <= xmax && ymin <= ymax) {
350     XSetForeground(st->dpy, st->gc, st->blackColor);
351     XFillRectangle(st->dpy, st->backbuffer, st->gc,
352                    xmin, ymin,
353                    xmax - xmin + 1, ymax - ymin + 1);
354     st->xmin = st->width + 1;
355     st->xmax = st->ymax = -1;
356     st->ymin = st->height + 1;
357   }
358
359   st->ccolour++;
360   st->ccolour %= st->ncolours;
361
362   /* calculate and draw points for this frame */
363   x = st->width << 7;
364   y = st->height << 7;
365   
366   if (st->multi) {
367     for (i = 0; i < st->lensnum; i++) {  
368       partcolor = st->ccolour * (i+1);
369       partcolor %= st->ncolours;
370       XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
371       memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
372       if (st->recurse)   
373         recurse(st, x, y, st->length - 1, i);
374       else
375         iterate(st, pow(st->lensnum, st->length - 1), i);
376       if (st->npoints) 
377         drawpoints(st);
378     }
379   } 
380   else {
381     
382     XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
383     memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
384     if (st->recurse)
385       recurse(st, x, y, st->length, 0);
386     else
387       iterate(st, pow(st->lensnum, st->length), 0);
388     if (st->npoints)
389       drawpoints(st);
390   }
391   
392   /* if we just drew into a buffer, copy the changed area (including
393    * erased area) to screen */
394   if (st->backbuffer != st->window
395       && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
396           || (xmin <= xmax && ymin <= ymax))) {
397     if (st->xmin < xmin) xmin = st->xmin;
398     if (st->xmax > xmax) xmax = st->xmax;
399     if (st->ymin < ymin) ymin = st->ymin;
400     if (st->ymax > ymax) ymax = st->ymax;
401     XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
402               xmin, ymin,
403               xmax - xmin + 1, ymax - ymin + 1,
404               xmin, ymin);
405   }
406
407   for(i = 0; i < st->lensnum; i++) {
408     mutate(st, &st->lenses[i]);
409   }
410
411   return st->delay;
412 }
413
414 static void
415 ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
416
417 static void *
418 ifs_init (Display *d_arg, Window w_arg)
419 {
420   struct state *st = (struct state *) calloc (1, sizeof(*st));
421   int i;
422   XWindowAttributes xgwa;
423         
424   /* Initialise all this X shizzle */
425   st->dpy = d_arg;
426   st->window = w_arg;
427
428   st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
429   st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
430   st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
431
432   XGetWindowAttributes (st->dpy, st->window, &xgwa);
433   ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
434         
435   st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
436   if (st->ncolours < st->lensnum)
437     st->ncolours = st->lensnum;
438   if (st->colours) free(st->colours);
439   st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
440   if (!st->colours) exit(1);
441   make_smooth_colormap (xgwa.screen, xgwa.visual, xgwa.colormap, 
442                         st->colours, &st->ncolours,
443                         True, 0, False);
444
445   /* Initialize IFS data */
446  
447   st->delay = get_integer_resource(st->dpy, "delay", "Delay");
448   st->length = get_integer_resource(st->dpy, "length", "Detail");
449   if (st->length < 0) st->length = 0;
450   st->mode = get_integer_resource(st->dpy, "mode", "Mode");
451
452   st->rotate    = get_boolean_resource(st->dpy, "rotate", "Boolean");
453   st->scale     = get_boolean_resource(st->dpy, "scale", "Boolean");
454   st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
455   st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
456   st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
457
458   st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
459   if (st->lenses) free (st->lenses);
460   st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
461   if (!st->lenses) exit(1);
462   for (i = 0; i < st->lensnum; i++) {
463     CreateLens(st,
464                myrandom(1)-0.5,
465                myrandom(1),
466                myrandom(4)-2,
467                myrandom(4)+2,
468                &st->lenses[i]);
469   }
470
471   return st;
472 }
473
474 static void
475 ifs_reshape (Display *dpy, Window window, void *closure, 
476              unsigned int w, unsigned int h)
477 {
478   struct state *st = (struct state *)closure;
479   XWindowAttributes xgwa;
480
481   /* oh well, we need the screen depth anyway */
482   XGetWindowAttributes (st->dpy, st->window, &xgwa);
483
484   st->width = xgwa.width;
485   st->widthb = ((xgwa.width + 31) >> 5);
486   st->height = xgwa.height;
487   st->width8 = xgwa.width << 8;
488   st->height8 = xgwa.height << 8;
489
490   if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
491     st->xmin = xgwa.width + 1;
492     st->xmax = st->ymax = -1;
493     st->ymin = xgwa.height + 1;
494   }
495
496   if (st->backbuffer != None && st->backbuffer != st->window) {
497     XFreePixmap(st->dpy, st->backbuffer);
498     st->backbuffer = None;
499   }
500
501   if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
502     st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
503     XSetForeground(st->dpy, st->gc, st->blackColor);
504     XFillRectangle(st->dpy, st->backbuffer, st->gc,
505                    0, 0, st->width, st->height);
506   } else {
507     st->backbuffer = st->window;
508     XClearWindow(st->dpy, st->window);
509   }
510
511   if (st->board) free(st->board);
512   st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
513   if (!st->board) exit(1);
514 }
515
516 static Bool
517 ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
518 {
519   struct state *st = (struct state *)closure;
520   if (screenhack_event_helper (dpy, window, event))
521     {
522       int i;
523       for (i = 0; i < st->lensnum; i++) {
524         CreateLens(st,
525                    myrandom(1)-0.5,
526                    myrandom(1),
527                    myrandom(4)-2,
528                    myrandom(4)+2,
529                    &st->lenses[i]);
530       }
531       return True;
532     }
533   return False;
534 }
535
536 static void
537 ifs_free (Display *dpy, Window window, void *closure)
538 {
539   struct state *st = (struct state *) closure;
540
541   if (st->board) free(st->board);
542   if (st->lenses) free(st->lenses);
543   if (st->colours) free(st->colours);
544   if (st->backbuffer != None && st->backbuffer != st->window)
545     XFreePixmap(st->dpy, st->backbuffer);
546   free(st);
547 }
548
549 XSCREENSAVER_MODULE ("IFS", ifs)